Поле GenericForeignKey
Поле GenericForeignKey не является стандартным полем базы данных, а представляет собой специальный класс в Django, который реализует универсальную связь между моделями. Оно не сохраняется напрямую в базе данных, а использует два других поля: поле для хранения идентификатора типа контента (ForeignKey на модель ContentType) и поле для хранения первичного ключа целевого объекта. При обращении к такому полю Django автоматически определяет нужную модель и загружает связанный объект.
Синтаксис
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class YourModel(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
Пример
Рассмотрим создание модели комментариев, которые могут быть привязаны к разным типам объектов (статьям, постам или продуктам):
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Comment(models.Model):
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
Пример
Теперь создадим комментарии для разных объектов:
from myapp.models import Article, Post, Product, Comment
article = Article.objects.create(title="First article", content="...")
post = Post.objects.create(title="First post", content="...")
product = Product.objects.create(name="Laptop", price=1000.00)
comment1 = Comment.objects.create(
text="Great article!",
content_object=article
)
comment2 = Comment.objects.create(
text="Interesting post",
content_object=post
)
comment3 = Comment.objects.create(
text="Nice product",
content_object=product
)
print(f"Comment 1: {comment1.content_object} - {comment1.text}")
print(f"Comment 2: {comment2.content_object} - {comment2.text}")
print(f"Comment 3: {comment3.content_object} - {comment3.text}")
Результат выполнения кода:
"Comment 1: Article object (1) - Great article!"
"Comment 2: Post object (1) - Interesting post"
"Comment 3: Product object (1) - Nice product"
Пример
Получим все комментарии для конкретной статьи:
from myapp.models import Article, Comment
from django.contrib.contenttypes.models import ContentType
article = Article.objects.get(id=1)
article_content_type = ContentType.objects.get_for_model(article)
comments = Comment.objects.filter(
content_type=article_content_type,
object_id=article.id
)
for comment in comments:
print(f"Comment: {comment.text}")
Результат выполнения кода:
"Comment: Great article!"
"Comment: Another great article!"
Пример
Используем обратную связь через поле GenericRelation:
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
comments = GenericRelation('Comment')
class Comment(models.Model):
text = models.TextField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
# Now we can get comments directly from article
article = Article.objects.get(id=1)
for comment in article.comments.all():
print(comment.text)
Результат выполнения кода:
"Great article!"
"Another great article!"
Смотрите также
-
поле
GenericRelation,
которое позволяет создавать обратную универсальную связь -
класс
prefetch_related_objects,
который используется для оптимизации запросов с универсальными связями -
класс
prefetch_related,
который применяется для предварительной загрузки связанных объектов -
класс
filter,
который используется для фильтрации записей вQuerySet