使用直通模型了解Django中的ManyToMany字段

Joh*_*han 8 django django-models

我无法理解ManyToMany模型字段与直通模型的使用.没有ManyToMany字段,我可以很容易地实现相同的功能.考虑以下Django的文档:

class Person(models.Model):
    name = models.CharField(max_length=128)


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')


class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
Run Code Online (Sandbox Code Playgroud)

我不明白的是,如何使用ManyToMany字段比简单地删除它并使用相关管理器更好.例如,这两个模型将更改为以下内容:

class Group(models.Model):
    name = models.CharField(max_length=128)


class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name='members')
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么?

Ale*_*kov 27

因此,我只是想向正在关注此问题并可能需要另一个示例来节省他们研究的人提供补充。一方面,我认为重要的是要注意,在OP的问题中,他应该删除模型Group而不是People模型,并从模型中删除匹配字段Membership。这样,模型就回到了它最初的含义。

当考虑多对多关系时,该through字段几乎可以被设计为多对多关系的“原因”。如果我们给命名法起一个不同的名称,可能会改变读者看到的内容:

class Person(models.Model):
    name = models.CharField(max_length=128)


class Club(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='RegistrationReceipt')


class RegistrationReceipt(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    club = models.ForeignKey(Club, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
    paid_dues = models.BooleanField(default = True)
    fee_payment_date = models.DateTimeField() 
Run Code Online (Sandbox Code Playgroud)

现在,您可以想象每当有成员加入该俱乐部时您自己都会添加各种逻辑。他们什么时候加入的?他们为什么加入?他们付钱了吗?他们的付款日期是什么时候?显然,您可以用不同的方式处理这种关系,但是您可以更清楚地看到“通过”在多对多关系中的使用。

另外,对于那些了解 SQL 的人来说。through属性/字段是您自定义中间表的方式,Django 自己创建的表,即through字段正在更改的内容。

  • 这应该是公认的答案,不仅是为了指出OP示例中的错误,而且还显示了显式直通表的真正好处 (7认同)

Kev*_*nry 12

是的,使用显式through表基本上消除了对a的需要ManyToManyField.

拥有它的唯一真正好处是,如果您发现相关经理方便.就是这样:

group.members.all()  # Persons in the group
Run Code Online (Sandbox Code Playgroud)

看起来比这更好:

Person.objects.filter(membership_set__group=group)  # Persons in the group
Run Code Online (Sandbox Code Playgroud)

在实践中,我认为两者兼顾的主要原因是人们常常以平原开始ManyToManyField; 意识到他们需要一些额外的数据并添加一个through表格; 然后由于使用两个管理器的代码而导致两者都结束.

  • 此外,m2m 关系与 through 保证关系的唯一性。在问题示例中,没有什么可以阻止具有相同个人/组引用的 2 个成员资格 (5认同)
  • @mirek:您在这里的批评以及下面的回答是基于对问题的误解。OP 不使用显式表进行比较。他们正在比较是否在存在这样的表的情况下使用 m2m 字段。因此,为了回答您的问题,两个版本中都存在“会员资格”。我稍微编辑了我的答案以使其更清晰,但实际上您只需要重新阅读问题即可。 (2认同)