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
Improving page speed score in Google Page Score test- PART2 Chaitanya Kattineni

In our previous blog post we got an idea of what are the things that Google's Page Speed Insights will take into count to give ...

Continue Reading...
Django Subdomains to do advanced things Ashwin Kumar

We always struggle to give users customization's even before they login to the system like abc.micropyramid.com and django don't know how to handle that out ...

Continue Reading...
Integrating Gitlab in django project with gitlab api for gitlab authentication and access Nikhila Mergu

This is a simple way to integrate gitlab authentication in your django apps.

We can get user verified email id, general information, git lab URL ...

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