I have read Django's documentation on GenericForeignKeys and found it to be very confusing. I don't understand how the relationship works, and how the variables I pass to it create said relationship - and everything I see only makes me more confused, because I still can't find anything clearly explaining, at its root, what it is and how it works.
Can someone please start from the beginning and explain what a GenericForeignKey
is, and how the relationship works?
We have to start with understanding the limitations of a ForeignKey
relationship; when creating a ForeignKey
field in a model, the model which defines the object that a ForeignKey
field will relate to has to be defined in the ForeignKey
field.
So for example, let's say we were making a social media platform where you could make posts and like posts, there would be a Post
model and a Like
model, and we would want each instance of the Like
model (every time someone likes a post) to have a ForeignKey
relationship to the object that was liked (i.e. the instance of the Post
model that was liked). So we could do this:
class Post(models.Model):
post = models.TextField()
class Like(models.Model):
liked_post = models.ForeignKey(Post, on_delete=models.CASCADE)
So the limitation of a ForeignKey
relationship is that an instance of a model can only have a ForeignKey
relationship with an instance of the model specified in the ForeignKey
field.
So let's say we had 2 types of models for posts; a TextPost
model and an ImagePost
model. We wouldn't be able to use a ForeignKey
field in the Like
model, because it can only be to an object which is an instance of 1 pre-specified model.
This is where a GenericForeignKey
field come in; essentially, it enables you to have a 'Foreign
' relationship to an instance of any other model.
But how does a GenericForeignKey
field work? Let's examine it:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class TextPost(models.Model):
post = models.TextField()
class ImagePost(models.Model):
post = upload = models.ImageField(upload_to ='uploads/')
class Like(models.Model):
post_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
post_id = models.PositiveIntegerField()
post = GenericForeignKey("post_content_type", "post_id")
Now, let's break this down. We define 2 variables, in this case post_content_type
and post_id
, and then we pass them to the GenericForeignKe
y field. So, let's first understand what we are defining and passing to the GenericForeignKey
:
post_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
We are defining post_content_type
as a ForeignKey
field. However, this ForeignKey
field is not to the model Post
model; rather, we are creating a ForeignKey
relationship to the instance of the ContentType
model of whichever model we are making a GenericForeignKey
relationship to.
As explained here, an instance of the ContentType
model is created for every model that you define in your models.py
file (not for every time you create an instance of a model). So what you are doing is making a ForeignKey
relationship between an instance of the Like
model to the instance of the ContentType
model corresponding to the Post
model (as shown here, the ContentType
instance for a model is <ContentType: app_name | model_name>
. So let's say our models were in the 'user_posts
' app, the TextPost
model's ContentType
instance would be <ContentType: user_posts | TextPost>
, and the ImagePost
model's ContentType
instance would be <ContentType: user_posts | ImagePost>
.
post_id = models.PositiveIntegerField()
Each instance of a model a model has a primary_key
which is an integer
. As explained here and here, if you make a model and do not specify a field to be the primary_key
(using primary_key=True
), Django creates a field called id
by default. So we are defining post_id
as a PositiveIntegerField
, which will be set to the primary_key
(meaning the id
field) of the instance of the model that we are making a GenericForeignKey
relationship to.
post = GenericForeignKey("post_content_type", "post_id")
We now define a GenericForeignKey
field, and pass it the ForeignKey
of the instance of the ContentType
model that was made for the model that the object which we are making a GenericForeignKey
relationship to is an instance of (in this case post_content_type
), and the primary_key
of the object that we are making a GenericForeignKey
relationship to (in this case post_id
).
So now, to answer the question:
Can someone please start from the beginning and explain what a
GenericForeignKey
is, and how it works?
A GenericForeignKey
is a field which lets any you have a 'Foreign' relationship to an instance of any model (i.e. a GenericForeign
relationship), using:
ForeignKey
relationship to the instance of the ContentType
model that corresponds to the model of which the object you are trying to make a relationship to is an instance ofprimary_key
of the specific object you are making a relationship to