Reversing a unique Generic Foreign Key with Django
When using GenericRelations with Django to create unique generic foreign keys between objects, it can become tiresome having to navigate the RelatedManager every time you need to retrieve the single instance of the one-to-one relationship. This post shows how creating a quick Mixin
can help overcome this inconvenience.
Update
This is an old post. It might not still be the best way to do things. Buyer beware.
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):
pass
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