Атрибут to_attr
Атрибут to_attr применяется к экземпляру класса Prefetch и позволяет задать пользовательское имя атрибута, в который будут сохранены результаты предварительной загрузки связанных объектов. В отличие от стандартного поведения prefetch_related, где связанные объекты добавляются к родительской модели под тем же именем, что и поле отношения, использование to_attr дает возможность явно управлять именами атрибутов. Это особенно полезно при работе с несколькими Prefetch объектами для одной и той же связи или при необходимости загрузить связанные объекты с дополнительными условиями фильтрации.
Синтаксис
from django.db import models
from django.db.models import Prefetch
queryset = Model.objects.prefetch_related(
Prefetch('related_field', queryset=RelatedModel.objects.all(), to_attr='custom_name')
)
Пример . С моделью Category и Article
Рассмотрим модели Category и Article, где у категории может быть много статей. Используем to_attr, чтобы сохранить все статьи категории в атрибут 'articles_list':
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='articles')
is_published = models.BooleanField(default=True)
def __str__(self):
return self.title
Теперь выполним запрос с использованием Prefetch и атрибута to_attr:
from myapp.models import Category
from myapp.models import Article
from django.db.models import Prefetch
prefetch = Prefetch('articles', queryset=Article.objects.filter(is_published=True), to_attr='published_articles')
categories = Category.objects.prefetch_related(prefetch)
for category in categories:
print(f"Category: {category.name}")
for article in category.published_articles:
print(f" - {article.title}")
Результат выполнения кода:
"Category: Technology"
" - Django Basics"
" - Python Tips"
"Category: Science"
" - Quantum Physics"
Пример . С множественными Prefetch
Рассмотрим пример, где для одной связи используются два разных Prefetch объекта с разными условиями фильтрации:
from myapp.models import Category
from myapp.models import Article
from django.db.models import Prefetch
published = Prefetch('articles', queryset=Article.objects.filter(is_published=True), to_attr='published_articles')
drafts = Prefetch('articles', queryset=Article.objects.filter(is_published=False), to_attr='draft_articles')
categories = Category.objects.prefetch_related(published, drafts)
for category in categories:
print(f"Category: {category.name}")
print(f" Published: {len(category.published_articles)}")
print(f" Drafts: {len(category.draft_articles)}")
Результат выполнения кода:
"Category: Technology"
" Published: 2"
" Drafts: 1"
"Category: Science"
" Published: 1"
" Drafts: 0"
Пример . С использованием только to_attr
Атрибут to_attr можно использовать самостоятельно, без указания дополнительных фильтров в queryset:
from myapp.models import Category
from myapp.models import Article
from django.db.models import Prefetch
prefetch = Prefetch('articles', to_attr='all_articles')
categories = Category.objects.prefetch_related(prefetch)
for category in categories:
print(f"{category.name}: {len(category.all_articles)} articles")
Результат выполнения кода:
"Technology: 3 articles"
"Science: 1 articles"
Смотрите также
-
класс
Prefetch,
который используется для создания объекта предварительной загрузки -
метод
prefetch_related,
который загружает связанные объекты отдельным запросом -
метод
select_related,
который загружает связанные объекты через JOIN запрос -
функция
prefetch_related_objects,
которая позволяет предварительно загрузить объекты в уже существующий список