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
Integration Of GitHub API with python django Nikhila Mergu

Using Github integration, we can get the user verified email id, general information, git hub URL, id, disk usage, public, private repo's, gists and followers, ...

Continue Reading...
Querying with Django Q objects Dinesh Deshmukh

Querying with Django Q objects: Q object encapsulates a SQL expression in a Python object that can be used in database-related operations. Using Q objects ...

Continue Reading...
Autocomplete with Django-Haystack and Elasticsearch with single letter querying. Ashwin Kumar

Django's haystack provides autocomplete functionality. To do autocomplete effectively, the search backend(elasticsearch in this case) uses n-grams (essentially a small window passed over the string). ...

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