Search code examples
pythondjangodjango-modelsmany-to-many

Django how to populate fields in 'trough' intermediate model when adding an object to a ManyToManyField?


I couldn't word it properly nor could I find it in the documentation, but how do I also add to the intermediate 'through' model of a ManyToManyField when adding new objects to it?

Note that its a recursive relationship, but I used ManyToMany as I was unsure if OneToOne supports a "through" model (the documentation does not specifies it)

Also Person_Person will be used regardless of the curr_partner field, adding objects like parents/child relationships to it (which have no field in Person) (This was due to the confusion that ManyToManyField was an actual field in the output table in a SQL relational database)

(I realize that my model has some contextual/theoretical flaws, but let's abstract that for now)

For example

Models.py :

class Person(models.Model):
    objectid = models.AutoField(primary_key=True)
    name = models.CharField()
    curr_partner = models.ManyToManyField(
        self, 
        on_delete = models.CASCADE, 
        through = Person_Person, #This lets you define the model that will act as an intermadiary
        symmetrical = False, #This needs to be set with recursive relationships
    )

class Person_Person(models.Model):
    person_1 = models.ForeignKey(Person, ondelete=models.CASCADE)
    person_2 = models.ForeignKey(Person, ondelete=models.CASCADE)
    relation = models.ChoiceField(            
        ('P', 'Parent'),
        ('C', 'Child'),
        ('E', 'Engaged'),
        ('W', 'Widow'),
    )

Query :

#Adding a married couple

father = Person(name = "John")
mother = Person(name = "Anna")

father.curr_partner.add( mother , through.relation = "Engaged") #???

#Adding a 'Child' relation

child = Person(name = "Billy")

#This makes sense??
p1 = Person_Person(person1 = father, person2 = child, relation = "Child")
p2 = Person_Person(person1 = mother, person2 = child, relation = "Child")

Solution

  • You create the relationships by saving instances of the relationship's model, like you (almost) did with the children. You'd have something like

    father = Person(name = "John")
    mother = Person(name = "Anna")
    
    marriage = Person_Person.objects.create(person1=father, person2=mother, relation="E")
    
    p1 = Person_Person.objects.create(person1=father, person2=child, relation="C")
    p2 = Person_Person.objects.create(person1 = mother, person2 = child, relation="C")
    

    BTW, your curr_partner attribute doesn't have an accurate name, since it actually denotes all of a person's relationships, not just marriage. Maybe you should rename it to relationships or kindred and have a property to get the person's partner:

    @property
    def curr_partner(self):
        try:
            return self.relationships.objects.get(relation='E')
        except Person_Person.DoesNotExist:
            # No marriage relationship
            return