Django 1.8 - 中间多对多关系 - 使用“ManytoManyField”的结果是啥?

Posted

技术标签:

【中文标题】Django 1.8 - 中间多对多关系 - 使用“ManytoManyField”的结果是啥?【英文标题】:Django 1.8 - Intermediary Many-to-Many-Through Relationship - What is the consequence of where 'ManytoManyField' is used?Django 1.8 - 中间多对多关系 - 使用“ManytoManyField”的结果是什么? 【发布时间】:2015-10-17 19:40:14 【问题描述】:

Django 中的多对多关系示例:

class First(models.Model):
    seconds = models.ManyToManyField(Second, through='Middle')

class Middle(models.Model):
    first = models.ForeignKey(First)
    second = models.ForeignKey(Second)

class Second(models.Model):

在documentation on intermediary models 之后,要关联的对中只有一个模型包含ManytoManyField,上面示例中的模型First。这是正确的吗?

如果是这样,哪个模型应该包含ManytoManyField 字段?根据ManytoManyField 的位置,使用两端的关系有​​什么不同吗?

谢谢

编辑(我应该更清楚):

我对中间表感兴趣,因为我将有额外的数据来存储关系。

当我说用法时,我不是指定义模型,我指的是使用关系(否则我会让 Django 做这件事)。

如果我希望所有秒与第一个相关,它是否与将所有第一个与第二个相关完全一样,或者ManytoManyField 是否会通过引入任何额外功能使一个方向比另一个方向更容易实现?

【问题讨论】:

【参考方案1】:

从操作的角度来看应该没有区别,所以唯一的区别在于模型的定义和影响它的事物(例如,Manager 类)。

您也不必总是定义“通过”类。 Django 会自动为您执行此操作,而该类真正所做的只是维护第三个表来跟踪另外两个表中每个相关记录的相应 ID。您必须决定是否要向第三个表中添加任何重要的内容。

例如,假设您正在为会议设计一个网络应用程序。他们可能想要存储有关与会者(个人和公司)以及演讲者和赞助商(也包括个人和公司)的信息。您的部分公司模型可能如下所示:

class Company(models.Model):
    name = models.CharField(max_length=100)
    sponsored_segment = models.ForeignKey(ConferenceSegment, null=True)

class ConferenceSegment(models.Model):
    title = models.CharField(max_length=100)

但这很快就会变得很麻烦,而且您将有很多与赞助无关的参加公司。此外,您可能想在网站上跟踪他们的排名/套餐(毕竟,更大的赞助商获得更大的排名):

class Company(models.Model):
    name = models.CharField(max_length=100)

class ConferenceSegment(models.Model):
    title = models.CharField(max_length=100)
    sponsors = models.ManyToManyField(Company, through=u'Sponsor', related_name=u'sponsored_segments')

class Sponsor(models.Model):
    company = models.ForeignKey(Company)
    segment = models.ForeignKey(ConferenceSegment)
    rank = models.PositiveIntegerField()

还要注意ManyToManyField 中的“related_name”属性。这意味着我们可以通过 Company 实例使用该名称访问 ConferenceSegment 对象:

c = Company.objects.get(...)
segments = c.sponsored_segments.all()

希望这会有所帮助。

【讨论】:

如果没有相关名称,您会怎么做?似乎这是在一个模型中声明 ManytoManyField 而不是另一个模型的好处? segments = c.segments.all() 会工作吗?其他方式呢,s = Segment.objects.get(...) companies = s.companies.all() 1) 它将使用 Django 的默认命名。所以在上面的例子中,它将是conferencesegment_set 而不是sponsored_segmentsrelated_name 可帮助您使名称更适合关系。 2)当然,如果你想节省打字。 3) 是的,如果 segmentsrelated_name。 4) 正确的查询是s.sponsors.all() 知道了,所以根据ManyToManyField 的位置没有功能差异,唯一的优点是使用related_name 而不是Django 默认值。【参考方案2】:

当您向模型添加多对多字段时,会在数据库中创建一个单独的表,用于存储两个模型之间的链接。如果您不需要在第三个表中存储任何额外信息,那么您不必为它定义模型。

class First(models.Model):
    seconds = models.ManyToManyField(Second, related_name='firsts')

class Second(models.Model):
    pass

我想不出在第一个或第二个模型中定义多对多字段之间有什么区别:

class First(models.Model):
    pass

class Second(models.Model):
    firsts = models.ManyToManyField(First, related_name='seconds')

这两种情况的用法是一样的:

firsts = my_second.firsts
seconds = my_first.seconds

【讨论】:

在使用关系/查询方面的使用情况如何?

以上是关于Django 1.8 - 中间多对多关系 - 使用“ManytoManyField”的结果是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Django:通过参数序列化具有多对多关系的模型

django多对多中间表详解

Django多对多关系建立及Form组件

Django dumpdata 和 loaddata 不适用于多对多中间模型

Hibernate学习笔记 --- 创建基于中间关联表的多对多映射关系

为核心数据手动创建多对多关系中的中间表