[Python] Django: How to Represent and Query Symmetrical Relationships for a Family Tree?

Discussão em 'Python' iniciado por Stack, Janeiro 24, 2025.

  1. Stack

    Stack Membro Participativo

    I am building a family tree application in Django where I need to represent and query marriages symmetrically. Each marriage should have only one record, and the relationship should include both partners without duplicating data. Here's the relevant model structure:

    class Person(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    spouses = models.ManyToManyField(
    'self', through="Marriage", symmetrical=True, related_name="partners"
    )


    class Marriage(models.Model):
    person1 = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="marriages_as_person1")
    person2 = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="marriages_as_person2")
    start_date = models.DateField(null=True, blank=True)
    end_date = models.DateField(null=True, blank=True)


    I want to:

    1. Ensure both partners appear as spouses for each other symmetrically.
    2. Avoid duplicate entries for the same marriage.
    3. Efficiently query all spouses of a person.

    Here’s the code I’m using to query spouses:

    # Query spouses for a person
    p1 = Person.objects.create()
    p2 = Person.objects.create()
    Marriage.objects.create(person1=p1, person2=p2)

    p1.spouses.all() # Returns list containing p2
    p2.spouses.all() # Returns empty list


    However, I’m facing challenges:

    1. If p1 spouses are queried, it should contain p2 and if p2 spouses are queried, it should contain p1
    2. Both queries are not symmetrical
    Questions:

    1. Is my model structure correct for representing marriages symmetrically? If not, what improvements should I make?
    2. How can I efficiently query all spouses of a person in a database-optimized way while ensuring symmetry?

    My use case is to return a list or Person having pids (partner id) as a list of id like below using DRF

    [
    {
    id: 1,
    full_name: ‘John’,
    pids: [2]
    },
    {
    id: 2,
    full_name: ‘Mary’,
    pids: [1]
    }
    ]


    Current serializer code is

    def get_pids(self, obj):
    """
    Returns a list of IDs of all the spouses of the person, ensuring bidirectional relationships.
    """
    partner_ids = set(obj.spouses.values_list('id', flat=True))
    # Ensure bidirectional relationships
    for spouse in obj.spouses.all():
    partner_ids.update(spouse.spouses.values_list('id', flat=True))

    return list(partner_ids)

    Continue reading...

Compartilhe esta Página