Reversing a unique Generic Foreign Key with Django

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()[0]
$ 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