7. How to create a generic model which can be related to any kind of entity? (Eg. a Category or a Comment?)¶
You have models like this.
class Category(models.Model):
name = models.CharField(max_length=100)
# ...
class Meta:
verbose_name_plural = "Categories"
class Hero(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
# ...
class Villain(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
# ...
Category
can be applied as a generic model. You prbably want to be able to apply categories to objects form any model class.
You can do it like this
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
# ...
class FlexCategory(models.Model):
name = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Hero(models.Model):
name = models.CharField(max_length=100)
flex_category = GenericRelation(FlexCategory, related_query_name='flex_category')
# ...
class Villain(models.Model):
name = models.CharField(max_length=100)
flex_category = GenericRelation(FlexCategory, related_query_name='flex_category')
# ...
What did we do, we added a GenericForeignKey
fields on FlexCategory
using one ForeignKey
and one PositiveIntegerField
, then
added a GenericRelation
on the models you want to categorize.
At the database level it looks like this:
You can categorize a Hero
like this.
FlexCategory.objects.create(content_object=hero, name="mythic")
And then get a Hero
categorised as ‘ghost’ like this
FlexCategory.objects.create(content_object=hero, name="ghost")
This gives us this sql.
SELECT "entities_hero"."name"
FROM "entities_hero"
INNER JOIN "entities_flexcategory" ON ("entities_hero"."id" = "entities_flexcategory"."object_id"
AND ("entities_flexcategory"."content_type_id" = 8))
WHERE "entities_flexcategory"."name" = ghost