Question

I have a model called Person and a person may or may not have several relationship; spouse, children, parents. If the Person has a spouse, but that spouse isn't a person in the database, I want to be able to keep the spouse as just a string value and not another Person. Because of that I assume I need a through model specified on the m2m relationships:

class Person(models.Model):
    spouse = models.ManyToManyField('self', through='Relationship')
    children = models.ManyToManyField('self', through='Relationship')
    parents = models.ManyToManyField('self', through='Relationship')

class Relationship(models.Model):
    personA = models.ForeignKey(Person)
    personB = models.ForeignKey(Person, blank=True, null=True)
    person_name = models.TextField(default='', blank=True)
    relationship = models.CharField(choices=(('s', 'spouse'),
                                             ('c', 'child'),
                                             ('p', 'parent')),
                                    default='s')

When I try to run the ./manage.py migrate I get the following error messages showing up:

CommandError: One or more models did not validate:
myapp.person: Many-to-many fields with intermediate tables cannot be symmetrical.
myapp.person: Many-to-many fields with intermediate tables cannot be symmetrical.
myapp.person: The model Person has two manually-defined m2m relations through the model Relationship, which is not permitted. Please consider using an extra field on your intermediary model instead.
myapp.person: Many-to-many fields with intermediate tables cannot be symmetrical.
myapp.person: The model Person has two manually-defined m2m relations through the model Relationship, which is not permitted. Please consider using an extra field on your intermediary model instead.
myapp.relationship: Accessor for field 'personA' clashes with related field 'Person.relationship_set'. Add a related_name argument to the definition for 'personA'.
myapp.relationship: Accessor for field 'personB' clashes with related field 'Person.relationship_set'. Add a related_name argument to the definition for 'personB'.

Obviously I'm doing it wrong.

How can I specify relationships between the same model with extra data associated with that relationship?

Alternatively, how else might you structure these models to keep track of the same data?

Was it helpful?

Solution

Error creating several recursive m2m relationships seems like a related question. I realized that I could get the same effect by removing the parents, children, spouse fields from the Person model and setting the related_name to relationships on the Relationship model.

class Person(models.Model):
    [ other fields ]

class Relationship(models.Model):
    personA = models.ForeignKey(Person, related_name='relationships')
    personB = models.ForeignKey(Person, blank=True, null=True)
    person_name = models.TextField(default='', blank=True)
    relationship = models.CharField(choices=(('s', 'spouse'),
                                             ('c', 'child'),
                                             ('p', 'parent')),
                                    default='s')

I haven't tested it yet, but it does validate and doesn't throw the error messages any more. This also seems cleaner.

I imagine I could create some properties on the Person model to give me access to say Person.spouse which returns all Person.relationships where the relationship == 's'.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top