Django’s generic foreign keys makes it very easy to create a unique generic object that can be attached to any model instance. The following is a contact card that we can attach to people or users:
class ContactDetails(models.Model): phone_number = models.CharField(...) address = models.CharField(...) #... content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') class Meta: unique_together = ('content_type', 'object_id',) class User(models.Model): #... contacts = generic.GenericRelation(ContactDetails, object_id_field="object_pk")
$ user = User.objects.get(pk=1) $ contact_card = user.contacts.all() $ print(contact_card.phone_number)
This works, but it’s a little bulky as we have to navigate the
RelatedManager every time we want to return the object on the other end of our one-to-one relationship. A simple solution is to create a mixin that will make this transparent:
class ContactMixin(object): @property def contact_card(self): ctype = ContentType.objects.get_for_model(self.__class__) try: contact = Contact.objects.get(content_type__pk = ctype.id, object_id=self.id) except Contact.DoesNotExist: return None return contact class User(models.Model, ContactMixin): # ...
Now we don’t have to meddle with the related manager; we can instead access our instance object directly:
$ user = User.objects.get(pk=1) $ print user.contact_card.phone_number