Kurt Peek
Kurt Peek

Reputation: 57641

Adding additional attributes to a Django field

Consider this Family model in Django:

class Family(models.Model):
    EMPLOYEE = 'Employee'
    PARTNER = 'Partner'
    BIRTH_PARENT_CHOICES = (
       (EMPLOYEE, EMPLOYEE),
       (PARTNER, PARTNER),
    )
    employee_user = models.OneToOneField(User, blank=True, null=True, related_name='employee_family')
    partner_user = models.OneToOneField(User, blank=True, null=True, related_name='partner_family')
    employee_first_name = models.CharField(max_length=255, blank=True)
    employee_last_name = models.CharField(max_length=255, blank=True)
    employee_email = models.CharField(max_length=255, blank=True)
    employee_phone = models.CharField(max_length=255, blank=True)
    partner_first_name = models.CharField(max_length=255, blank=True)
    partner_last_name = models.CharField(max_length=255, blank=True)
    partner_email = models.CharField(max_length=255, blank=True)
    partner_phone = models.CharField(max_length=255, blank=True)
    point_of_contact = models.CharField(max_length=255, choices=BIRTH_PARENT_CHOICES)

A Family consists of an employee and a partner, both of which have various attributes (user, first name, last name, email, phone). There is also a point_of_contact field which is either 'Employee' or 'Partner'.

What I'd like to be able to do is to, on an instance family of Family, do something like

family.point_of_contact.phone_number

which would resolve to family.employee_phone_number if family.point_of_contact == Family.EMPLOYEE and family.partner_phone_number otherwise, and similarly for first_name, last_name, etc.

As far as I can tell from https://docs.djangoproject.com/en/2.0/ref/models/fields/, however, it isn't possible to define additional attributes on Django fields. Is there some other way I could do this?

Upvotes: 2

Views: 227

Answers (2)

Alex Hall
Alex Hall

Reputation: 36043

Django doesn't provide a way to do this, but you can do it with some simple Python:

from types import SimpleNamespace

class Family(SimpleNamespace):
    EMPLOYEE = 'employee'
    PARTNER = 'partner'

    @property
    def contact(self):
        return SimpleNamespace(**{
            attr: getattr(self, '%s_%s' % (self.point_of_contact, attr))
            for attr in 'first_name last_name'.split()
        })

family = Family(
    employee_first_name='Kurt',
    employee_last_name='Peek',
    partner_first_name='Jane',
    partner_last_name='Doe',
    point_of_contact=Family.EMPLOYEE,
)

print(family.contact.first_name)
print(family.contact.last_name)

Here SimpleNamespace is used in two ways:

  1. As a superclass of Family to make this example easy to test - skip that and stick to models.Model.
  2. In the contact property, keep that.

Upvotes: 1

little_birdie
little_birdie

Reputation: 5867

No, in order to do that, you would need to create a separate model Contact and join to it from Family using a OneToOneField if there can only be one contact per family, or using a ForeignKey in your Contact model if there can be more than one contact per family.

Upvotes: 1

Related Questions