Understanding 'GenericForeignKey' in Django

Reading Time : ~ .

In some cases we might want to store generic model object, rather a particular specific model as 'ForeignKey'. Here is the scenario of such kind. Suppose there are models like User, Project, Ticket and TimeLine as below.

class Ticket(models.Model):
    name = models.CharField(max_length=200, verbose_name=_("name"))
    slug = models.SlugField(max_length=250, null=False, blank=True, verbose_name=_("slug"))

class Project(models.Model):
    name = models.CharField(max_length=200, verbose_name=_("name"))
    slug = models.SlugField(max_length=250, null=False, blank=True, verbose_name=_("slug"))

class User(models.Model):
    name = models.CharField(max_length=200, verbose_name=_("name"))
    slug = models.SlugField(max_length=250, null=False, blank=True, verbose_name=_("slug"))

class Timeline(models.Model):
    involved_object = *****
    event_type = models.CharField(max_length=250, default="created")

If we want to store the user activities with these models like "created User, created Project, created Task" in a timeline, we have to create all the three models(User, Project, Task) as 'ForeignKey' fields. This is not a good programming practice. To overcome this, Django's content types framework provides a special field type (GenericForeignKey) which allows the relationship to be with any model. 

Using 'GenericForeignKey':

Here is the solution for the above scenario. There are three parts to setting up a GenericForeignKey:

  • Give your model a ForeignKey to ContentType. The usual name for this field is “content_type”.
  • Give your model a field that can store primary key values from the models you’ll be relating to. For most models, this means a PositiveIntegerField. The usual name for this field is “object_id”.
  • Give your model a GenericForeignKey, and pass it the names of the two fields described above. If these fields are named “content_type” and “object_id”, you can omit this – those are the default field names GenericForeignKey will look for.

So by following the above three steps, the TimeLine will look as following.

class Timeline(models.Model):
    content_type = models.ForeignKey(ContentType, related_name="content_type_timelines")
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    event_type = models.CharField(max_length=250, default="created")

For example, if we want to store the event "created Project" after the project is created, the following snippet will do things for us.

t1 = TimeLine(content_object=project_object)
t1.save()

But due to the way 'GenericForeignKey' is implemented, you cannot use such fields directly with filters (filter() and exclude(), for example) via the database API. Because a GenericForeignKey isn’t a normal field object, the following examples will not work:

TimeLine.objects.filter(content_object=project_object)
# This will also fail
TimeLine.objects.get(content_object=project_object)

To retrieve the TimeLine object with the project object, we have to follow these steps.

  • Get the 'ContentType' object with the following code. 
    from django.contrib.contenttypes.models import ContentType
        contenttype_obj = ContentType.objects.get_for_model(project_object)
  • "object_id" is stored with project_object.id
  • TimeLine.objects.filter(object_id=project_object.id, content_type=contenttype_obj)
    By Posted On
SENIOR DEVELOPER at MICROPYRAMID

Need any Help in your Project?Let's Talk

Latest Comments
Related Articles
Django-REST User Level Permissions and Object Level Permissions Anjaneyulu Batta

Django-REST User Level Permissions and Object Level Permissions. User Level Permissions and Object level Permissions allow to serve customers based on their access levels or ...

Continue Reading...
How to index binary files in django haystack Ramya Ambati

Now we are going to index text content which is stored in structured files such as PDFs, Microsoft Office documents, images, etc using haystack and ...

Continue Reading...
Understanding django serializers with examples Vamsi Popuri

Serializers are used for “translating” Django models into other formats like xmi,json,yaml(YAML Ain’t a Markup Language)

from django.core import serializers
data = serializers.serialize("xml", SomeModel.objects.all())

Continue Reading...

Subscribe To our news letter

Subscribe to our news letter to receive latest blog posts into your inbox. Please fill your email address in the below form.
*We don't provide your email contact details to any third parties