簡體   English   中英

如何使遞歸多對多關系與 Django 對稱

[英]How to make a recursive ManyToMany relationship symmetrical with Django

我已經閱讀了關於 Django Docs 的symmetrical=True 我也讀過這個問題,對舊版本的 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()包含ted ,而ted.friends.all()包含bill 這不是發生的事情。 bill的查詢包括ted ,但ted的查詢不包括 bill 。

>>> 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設置相同的行為。

添加關系的正確方法是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函數為字段分配適當的值。

文檔

當您在一個中間模型上有多個外鍵到參與多對多關系的任何(或什至兩個)模型時,您必須指定through_fields 當使用中間模型並且模型有兩個以上的外鍵時,這也適用於遞歸關系,或者您想明確指定應該使用哪兩個 Django。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM