Подтвердить что ты не робот

Как моделировать симметричные отношения с django?

Используйте классический пример друзей.

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) уникальны, и не нужно беспокоиться о (user2, user1), случайно появляющемся. Я должен был бы легко получить всех друзей данного пользователя, но вместо этого мне пришлось бы написать собственный менеджер или создать другой способ получения всех Друзей, в которых этот пользователь является user1 в отношениях, и всех Друзей, где этот пользователь является пользователем2.

Я подумываю о попытке написать свой собственный SymmetricKey. Кто-то, пожалуйста, остановите меня.

4b9b3361

Ответ 1

Проверьте symmetrical параметр ManyToManyField в документы - похоже, что он может делать то, что вы хотите.

Для конкретного способа, которым вы это делаете, я бы сделал что-то вроде

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

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

Ответ 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)

Обратите внимание на знак плюса в конце связанного_имя. Это указывает на 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)

Источник: Самостоятельная привязка многих ко многим через