Where should we use content types and generic relations in django?

(Comments)

Who is this tutorial for?

This tutorial assumes a basic knowledge of PYTHON and Django. Also it assumes you know how to create models, and manipulate them with basic querysets. If you are not confident about the aforementioned topics then this might help you.


Introduction

Generic Relations are a special type of relation that Django supports where the relationship (foriegn key) is not bound to one particular table. Thus with minimal coding (or hacking, in some cases) we could have a relationship that can handle cases like Likes and Follows in social media apps, where a post, page, person or group could be liked or followed.

Before getting on to an example and syntax, let's understand the concept of Content Types.


Content Types

Content types are Django's way of identifying database tables. Every Database table is represented as a row in the content type table which is created and maintained by Django. There are many operations and manipulations that you could do to a model using this Content Type module, but we will only be discussing here the portion that deals with Generic Relationships.

Since we have a Database representation of tables in these Content Type table, what Django does is use this to reference a table and then add an integer field to link to an id of that particular table, thus accomplishing a relationship coz we now the table, and its id.

Enough discussing the concept and let's delve into an example ans see the syntax along with it

Example Description

In the example let's try implementing the Like functionality of a typical social media app. The like can be associated with a Page, a Post, or a Comment. We assume the models for all these are implemented and not concern us with implementing them except the Like model.


Installation

First and foremost we should include the content type module to use them in our generic relation. So add the following in INSTALLED_APPS in settings.py

# Python code
# settings.py

INSTALLED_APPS = [
    ...

    'django.contrib.contenttypes',

    ...
]


Using Generic Relations

Lets's first look at the Like model and define the Generic Relations in it and then look at its explanation.

# Python code
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Like(models.Model):

    liked_by = models.ForeignKey(User)
    created_at = models.DateTimeField(auto_now_add=True)

    # Listed below are the mandatory fields for a generic relation
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()

Here the content_type is the reference to the content type or the table used for the relation. The object_id is used to store the id of the row, or kind of like a foreign key (not a foreign key though!). The content_object property on the other hand is used as a direct reference to the object to which the like enty is related to. This content_object field is actually just a property for the Like object for easy reference, and not stored in the database.

Once the Like model is done we need to specify which models can have a generic relation with a Like object. In our case we want the Post, Page and Comment models to be able to have generic relations to Like. So we insert the following lines of code into its' models.

# Python Code

from django.db import models
from django.contrib.contenttypes.fields import GenericRelation


class Post(models.Model):
    ...
    likes = GenericRelation(Like)

class Page(models.Model):
    ...
    likes = GenericRelation(Like)

class Comment(models.Model):
    ...
    likes = GenericRelation(Like)

Now let's see how to add likes for a post. We can do it in different ways:

# Get the post object
post = Post.objects.get(pk=1)

# Add a like for the post
post.likes.create(liked_by=request.user)

# Or in a similar way using the Like model to add the like
Like.objects.create(content_object=post, liked_by=request.user)

You could make your life easier by adding a reverse reference or reverse_query_name as Django calls it, to the GenericRelation definition. And query the Like model using this reverse query name as a lookup in the queryset.

# Python Code

from django.db import models
from django.contrib.contenttypes.fields import GenericRelation


class Post(models.Model):
    ...
    posted_by = models.ForeignKey(User)
    likes = GenericRelation(Like, related_query_name='post'))

...
...
...

Like.objects.filter(post__posted_by__first_name='Bob')

For detailed information on reverse_query_name refer the official Django Documentation

Comments

Recent Posts

Archive

2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom