【问题标题】:How do I model a symmetric relationship with django?如何用 django 建模对称关系?
【发布时间】:2010-11-04 16:12:37
【问题描述】:

让我们用朋友的经典例子。

class Friendship(models.Model):
    user1 = models.ForeignKey(User, related_name='friends1')
    user2 = models.ForeignKey(User, related_name='friends2')
    handshakes = models.PositiveIntegerField()
    hugs = models.PositiveIntegerField()
    # other silly data

友谊中的两个朋友(user1 和 user2)应该完全相等。我应该可以说 (user1, user2) 是 unique_together 并且不必担心 (user2, user1) 意外出现。我应该能够轻松地获得给定用户的所有朋友,但是我必须编写一个自定义管理器或创建一些其他方式来获取该用户在关系中是 user1 的所有友谊,以及所有友谊在哪里该用户是 user2。

我正在考虑尝试编写自己的 SymmetricKey。有人请阻止我。

【问题讨论】:

标签: django django-models


【解决方案1】:

查看the docsManyToManyFieldsymmetrical 选项——听起来它可以为所欲为。

对于你这样做的具体方式,我会做类似的事情

class LameUserExtension(User):
    friends = ManyToManyField("self", through=Friendship)

class Friendship(models.Model):
    # the stuff you had here

【讨论】:

  • 我实际上已经考虑过这个,但它听起来不像现在那么好。我真的不想使用多个模型来实现这一点,但我想这不是一个糟糕的解决方案。
  • 哦,我也要把标题改成对称,因为我的意思是这样,而不是自反。
  • 事实证明这行不通。来自文档:“在定义从模型到自身的多对多关系时,使用中间模型,您必须使用symmetric=False”
  • 我知道这已经有好几年了,但是这篇 2009 年的帖子没有告诉你如何做你想做的事吗?还是我答错了问题? caktusgroup.com/blog/2009/08/14/…
【解决方案2】:

我发现一篇不错的文章讨论了前段时间,基础知识如下:

class Person(models.Model):
    name = models.CharField(max_length=100)
    relationships = models.ManyToManyField('self', through='Relationship', 
                                           symmetrical=False, 
                                           related_name='related_to+')

RELATIONSHIP_FOLLOWING = 1
RELATIONSHIP_BLOCKED = 2
RELATIONSHIP_STATUSES = (
    (RELATIONSHIP_FOLLOWING, 'Following'),
    (RELATIONSHIP_BLOCKED, 'Blocked'),
)

class Relationship(models.Model):
    from_person = models.ForeignKey(Person, related_name='from_people')
    to_person = models.ForeignKey(Person, related_name='to_people')
    status = models.IntegerField(choices=RELATIONSHIP_STATUSES)

注意related_name 末尾的加号。这向 Django 表明不应该暴露反向关系。由于关系是对称的,所以这是期望的行为,毕竟,如果我和 A 是朋友,那么 A 是我的朋友。 Django 不会为您创建对称关系,因此需要在 add_relationship 和 remove_relationship 方法中添加一些内容来显式处理关系的另一端:

def add_relationship(self, person, status, symm=True):
    relationship, created = Relationship.objects.get_or_create(
        from_person=self,
        to_person=person,
        status=status)
    if symm:
        # avoid recursion by passing `symm=False`
        person.add_relationship(self, status, False)
    return relationship

def remove_relationship(self, person, status, symm=True):
    Relationship.objects.filter(
        from_person=self, 
        to_person=person,
        status=status).delete()
    if symm:
        # avoid recursion by passing `symm=False`
        person.remove_relationship(self, status, False)

现在,每当我们创建单向关系时,都会创建(或删除)它的补码。由于关系是双向的,我们可以简单地使用:

def get_relationships(self, status):
    return self.relationships.filter(
        to_people__status=status, 
        to_people__from_person=self)

来源:Self-referencing many-to-many through

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 2017-08-18
    相关资源
    最近更新 更多