【问题标题】:How to make a recursive ManyToMany relationship symmetrical with Django如何使递归多对多关系与 Django 对称
【发布时间】:2021-01-28 10:49:31
【问题描述】:

I have read the Django Docs regarding symmetrical=True. 我也读过this question asking the same question for an older version of Django,但以下代码不像 Django 文档描述的那样工作。

# people.models
from django.db import models


class Person(models.Model):
    name = models.CharField(max_length=255)
    friends = models.ManyToManyField("self",
                                     through='Friendship',
                                     through_fields=('personA', 'personB'),
                                     symmetrical=True,
                                     )

    def __str__(self):
        return self.name


class Friendship(models.Model):
    personA = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='personA')
    personB = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='personB')
    start = models.DateField(null=True, blank=True)
    end = models.DateField(null=True, blank=True)

    def __str__(self):
        return ' and '.join([str(self.personA), str(self.personB)])

如果billted 是朋友,我希望bill.friends.all() 包括tedted.friends.all() 包括bill。这不是发生的事情。 bill的查询包含ted,但ted的查询不包含账单。

>>> from people.models import Person, Friendship
>>> bill = Person(name='bill')
>>> bill.save()
>>> ted = Person(name='ted')
>>> ted.save()
>>> bill_and_ted = Friendship(personA=bill, personB=ted)
>>> bill_and_ted.save()
>>> bill.friends.all()
<QuerySet [<Person: ted>]>
>>> ted.friends.all()
<QuerySet []>
>>> ted.refresh_from_db()
>>> ted.friends.all()
<QuerySet []>
>>> ted = Person.objects.get(name='ted')
>>> ted.friends.all()
<QuerySet []>

这是一个错误还是我误解了什么?

编辑:更新代码以显示行为与 through_fields 设置相同。

【问题讨论】:

    标签: python django django-models many-to-many django-orm


    【解决方案1】:

    添加关系的正确方法是bill.friends.add(ted)。这将使billted 成为朋友,tedbill 成为朋友。如果您想为中间模型上的额外字段设置值,在我的情况下为startend,请为add() 使用through_defaults 参数。

    ...
    >>> bill.friends.add(ted, through_defaults={'start': datetime.now()}
    

    在某些情况下,您希望 bill -> ted 之间的关系在中间模型上具有与 ted -> bill 不同的值。例如,bill 认为 ted 很“酷”,当他们第一次见面时,ted 认为 bill 是“卑鄙的”。在这种情况下,您将需要辅助函数。

    # people.models
    from django.db import models
    
    
    class Person(models.Model):
        name = models.CharField(max_length=255)
        friends = models.ManyToManyField("self", through='Friendship')
    
        def __str__(self):
            return self.name
    
        def add_friendship(self, person, impressionA, impressionB, recursive=True):
            self.friends.add(person, through_defaults={'personA_impression': impressionA, 'personB_impression': impressionB)
            if recursive:
                person.add_friendship(self, impressionB, impressionA, False)
    
    class Friendship(models.Model):
        personA = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='a')
        personB = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='b')
        personA_impression = models.CharField(max_length=255)
        personB_impression = models.CharField(max_length=255)
    
        def __str__(self):
            return ' and '.join([str(self.personA), str(self.personB)])
    

    调用 bill.friends.add(ted, through_defaults={"personA_impression": "cool", "personB_impression": "mean"}) 会产生以下结果:

    ...
    >>> bill_and_ted = Friendship.objects.get(personA=bill)
    >>> ted_and_bill = Friendship.objects.get(personA=ted)
    >>> bill_and_ted.personA_impression
    "cool"  # bill thinks ted is cool
    >>> bill_and_ted.personB_impression
    "mean"  # ted thinks bill is mean
    >>> ted_and_bill.personA_impression
    "cool"  # ted thinks bill is cool. This contradicts the bill_and_ted intermediate model
    

    使用add_friendship 函数为字段分配正确的值。

    【讨论】:

      【解决方案2】:

      来自the documentation

      当您在参与多对多关系的任何(或什至两者)模型的中间模型上有多个外键时,您必须指定through_fields。这也适用于recursive relationships,当使用中间模型并且模型有两个以上的外键时,或者您想明确指定应该使用哪两个 Django。

      【讨论】:

      • 行为与through_fields设置相同。我已经更新了我的问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-15
      • 2011-06-02
      • 1970-01-01
      • 2015-02-27
      • 2015-03-02
      • 1970-01-01
      • 2020-07-25
      相关资源
      最近更新 更多