Overview
在本指南中,您可以学习;了解如何在 Django MongoDB后端中对集合之间的关系进行建模。由于MongoDB是一个文档数据库,因此 Django MongoDB后端提供了嵌入式模型字段,可将相关数据存储在单个文档中,而不是跨多个集合。 Django MongoDB后端还提供了一个大量字段,允许您在父文档中存储相关标量值的列表。
本指南解释了何时使用每种方法,并提供了示例来演示这些策略。
样本数据
本指南中的示例定义的模型表示 sample_mflix数据库中的以下集合:
movies:存储有关电影的信息embedded_movies:使用矢量图嵌入扩展moviesusers:存储有关电影观众的信息comments:存储有关电影评论的信息
sample_mflix要学习;了解有关 数据库的更多信息,请参阅MongoDB Atlas文档中的 示例 Mflix 数据集。
非规范化策略
关系数据库将数据规范化到单独的表中,并在查询时使用联接来组合相关数据。 MongoDB 的文档模型允许您将相关数据直接嵌入到父文档中,这称为非规范化。 Django MongoDB后端支持这两种方法,但我们建议您对数据进行非规范化以获得更好的性能。
要进行非规范化,请选择以下策略之一:
嵌入相关模型:使用
EmbeddedModelField或EmbeddedModelArrayField将相关数据存储在父文档中。嵌入式数据与父文档存储在同一MongoDB文档中,并在单个读取操作中检索。存储数组数据:使用 将相关标量值列表直接存储在父文档中。在单个读取操作中检索数组数据,无需
ArrayField$lookup操作。
嵌入相关模型
当应用以下所有条件时,请使用嵌入式模型:
相关数据始终与父文档一起读取。
相关数据属于单个父文档,不会在多个文档之间共享。
相关项目的数量是有限且可预测的。
要嵌入相关数据,请将嵌入式模型类定义为EmbeddedModel 的子类,然后使用 EmbeddedModelField 将其存储在父模型中。
以下示例定义了一个 Director 嵌入式模型,然后定义了一个存储 Director 模型实例的 Movie 模型:
from django.db import models from django_mongodb_backend.models import EmbeddedModel from django_mongodb_backend.fields import EmbeddedModelField class Director(EmbeddedModel): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Movie(models.Model): title = models.CharField(max_length=200) director = EmbeddedModelField(Director, null=True, blank=True)
存储数组数据
使用 ArrayField 将相关标量值的列表直接存储在父文档中。数组数据存储在同一个MongoDB文档中,并在单个读取操作中检索,而无需执行$lookup 操作。当应用以下条件时,请使用此策略:
相关值是简单标量,例如字符串或整数。
大量大小是有界且可预测的。
以下示例定义了一个 Movie 模型,用于在 cast字段中存储字符串列表:
from django.db import models from django_mongodb_backend.fields import ArrayField class Movie(models.Model): title = models.CharField(max_length=200) cast = ArrayField(models.CharField(max_length=100), blank=True)
要存储结构化文档数组而不是标量值,请改用 EmbeddedModelArrayField。要学习;了解更多信息,请参阅创建模型指南中的存储嵌入式模型数组数据。
关系字段
重要
跨关系字段的查询使用 MongoDB 的$lookup 操作符,这对于大型集合来说可能很慢。如果可能,请改用嵌入式模型。要学习;了解有关性能注意事项的更多信息,请参阅性能限制。
如果您的数据结构类似于关系数据库,并且您需要对大型分层数据集进行建模,则可以使用以下关系 Django 字段来链接不同集合中的文档:
重要
您不能在嵌入式模型类中使用关系字段,也不能将其用作 ArrayField 的 base_field。
外键
使用ForeignKey 字段在两个模型之间创建多对一关系。引用模型中的每个文档都链接到被引用模型中的一个文档。将以下参数传递给ForeignKey() 构造函数:
to:要链接到的模型类on_delete:删除引用文档时的删除行为
以下示例使用 ForeignKey字段将 Comment 模型链接到 Movie 模型。删除sample_mflix.movies文档时,所有相关的 sample_mflix.comments 文档也会被删除:
from django.db import models class Movie(models.Model): title = models.CharField(max_length=200) class Comment(models.Model): movie = models.ForeignKey( Movie, on_delete=models.CASCADE, ) text = models.TextField()
提示
要学习;了解有关on_delete 选项的更多信息,请参阅 Django 文档中的ForeignKey.on_delete。
OneToOneField
使用OneToOneField 字段在两个模型之间创建一对一关系。引用模型中的每个文档都链接到被引用模型中的一个文档。将以下参数传递给OneToOneField() 构造函数:
to:要链接到的模型类on_delete:删除引用的文档时的删除行为
以下示例定义了一个 EmbeddedMovie 模型,该模型使用 OneToOneField字段链接到 Movie 模型。删除sample_mflix.movies文档时,其链接的 sample_mflix.embedded_movies文档也会被删除:
from django.db import models from django_mongodb_backend.fields import ArrayField class Movie(models.Model): title = models.CharField(max_length=200) class EmbeddedMovie(models.Model): movie = models.OneToOneField( Movie, on_delete=models.CASCADE, ) plot_embedding = ArrayField(models.FloatField(), blank=True)
ManyToManyField
使用ManyToManyField 字段在两个模型之间创建多对多关系。任一模型中的每个文档都可以链接到另一个模型中的多个文档。将要链接的模型类作为第一个参数传递给ManyToManyField() 构造函数。
以下示例将 Viewer 模型链接到 Movie 模型。每个观看者可以观看多部电影,并且每部电影可以由多个观看者观看:
from django.db import models class Movie(models.Model): title = models.CharField(max_length=200) class Viewer(models.Model): name = models.CharField(max_length=100) email = models.CharField(max_length=200) watched = models.ManyToManyField(Movie, blank=True)
将关系数据转换为嵌入式数据
如果您有使用关系字段的现有模型并希望提高读取性能,则可以将其转换为使用嵌入式模型。这样就消除了 $lookup 操作,并将所有相关数据存储在单个MongoDB文档中。
此示例定义了一个具有以下关系字段的 Movie 模型:
ForeignKey,链接到DirectorOneToOneField,链接到AwardManyToManyField,链接到Writer
from django.db import models class Director(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Award(models.Model): wins = models.IntegerField(default=0) nominations = models.IntegerField(default=0) text = models.CharField(max_length=100) class Writer(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Movie(models.Model): title = models.CharField(max_length=200) director = models.ForeignKey( Director, null=True, on_delete=models.SET_NULL, ) awards = models.OneToOneField( Award, null=True, on_delete=models.SET_NULL, ) writers = models.ManyToManyField(Writer, blank=True)
要将这些关系字段转换为嵌入式字段,请更改每个相关模型以扩展 EmbeddedModel 而不是 models.Model,然后将 Movie 模型的关系字段替换为相应的嵌入式字段:
将
ForeignKey替换为EmbeddedModelField将
OneToOneField替换为EmbeddedModelField将
ManyToManyField替换为EmbeddedModelArrayField
以下示例显示了转换后的 Movie 模型:
from django.db import models from django_mongodb_backend.models import EmbeddedModel from django_mongodb_backend.fields import ( EmbeddedModelField, EmbeddedModelArrayField, ) class Director(EmbeddedModel): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Award(EmbeddedModel): wins = models.IntegerField(default=0) nominations = models.IntegerField(default=0) class Writer(EmbeddedModel): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) class Movie(models.Model): title = models.CharField(max_length=200) director = EmbeddedModelField(Director, null=True, blank=True) awards = EmbeddedModelField(Award, null=True, blank=True) writers = EmbeddedModelArrayField(Writer, blank=True)
更多信息
要学习;了解如何跨相关模型查询数据,请参阅指定查询指南中的高级字段查询。