user827080
user827080

Reputation:

Django OneToOneField with multiple of the same -- related_name

Sorry for the none-descriptive title, but I didn't really have a clue as to how to word this.

Say I have two models as such:

class Person(...):
  name = ... #have an attribute

class Family(...):
  mum = models.OneToOneField(Person)
  dad = models.OneToOneField(Person)

When I have a family containing mum and dad, I would think calling dad.family would yield me the family dad is in. However, I get an error message saying that this clashes with the mum attribute. The solution here is to use relative_names. But calling the family from mums side something else than from dads feels weird for me. WHy can't I just call dad.family? Could someone explain to me what exactly is clashing here?

Thanks!

Upvotes: 7

Views: 5033

Answers (2)

Lucius Silanus
Lucius Silanus

Reputation: 149

I know the answer above does answer the question of "why is it clashing", and I know it is an old question. However, I came across it trying to come up with a way to have the code restrict the entries in one column based on the entries in the other. I did not find a sufficient answer, but managed to puzzle it out myself. Below is an answer to a question that isn't directly asked, but may be helpful to others.

from django.db import models

class Person(models.Model):
  name = models.CharField(max_length=255)

class Family(models.Model):
  mum = models.OneToOneField(Person, on_delete=models.SET_NULL, related_name='family_as_mum', null=True)
  dad = models.OneToOneField(Person, on_delete=models.SET_NULL, related_name='family_as_dad', null=True)

  def save(self, *args, **kwargs):
    if Family.objects.filter(mum=self.dad).exists() or Family.objects.filter(dad=self.mum).exists() or self.mum == self.dad:
      return #any person can only be a mum or a dad, not both
    else:
      super(Family, self).save(*args, **kwargs)

  def update(self, *args, **kwargs):
    if Family.objects.filter(mum=self.dad).exists() or Family.objects.filter(dad=self.mum).exists() or self.mum == self.dad:
      return #same as save, don't want them to update a family member in an invalid way
    else:
      super(Family, self).update(*args, **kwargs)

  @staticmethod
  def get_family(person):
    if Family.objects.get(mum=person).exists():
      return Family.objects.get(mum=person)
    elif Family.objects.get(dad=person).exists():
      return Family.objects.get(dad=person)
    else:
      return None

*note, found the @staticmethod here, thought it might be useful to have a function to find the family associated with a parent, since we may not know up front if a person is a mum or a dad

Upvotes: 2

Ian Clelland
Ian Clelland

Reputation: 44152

The problem is that, given your model, a Person could be a mum to one Family, and a dad to a different family.

In that case, a query like this would be ambiguous:

pat = Person.objects.get(name='Pat')
family1.mum = pat
family1.save()

family2.dad = pat
family2.save()

pat.family # Which family do we want?

For that reason, you need to define a reverse relation name for each one (family_as_mum and family_as_dad, for instance)

Upvotes: 7

Related Questions