Атрибут to_attr

Атрибут to_attr применяется к экземпляру класса Prefetch и позволяет задать пользовательское имя атрибута, в который будут сохранены результаты предварительной загрузки связанных объектов. В отличие от стандартного поведения prefetch_related, где связанные объекты добавляются к родительской модели под тем же именем, что и поле отношения, использование to_attr дает возможность явно управлять именами атрибутов. Это особенно полезно при работе с несколькими Prefetch объектами для одной и той же связи или при необходимости загрузить связанные объекты с дополнительными условиями фильтрации.

Синтаксис

pythonfrom 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':

pythonfrom 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:

pythonfrom 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}")

Результат выполнения кода:

python"Category: Technology"
"  - Django Basics"
"  - Python Tips"
"Category: Science"
"  - Quantum Physics"

Пример . С множественными Prefetch

Рассмотрим пример, где для одной связи используются два разных Prefetch объекта с разными условиями фильтрации:

pythonfrom 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)}")

Результат выполнения кода:

python"Category: Technology"
"  Published: 2"
"  Drafts: 1"
"Category: Science"
"  Published: 1"
"  Drafts: 0"

Пример . С использованием только to_attr

Атрибут to_attr можно использовать самостоятельно, без указания дополнительных фильтров в queryset:

pythonfrom 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")

Результат выполнения кода:

python"Technology: 3 articles"
"Science: 1 articles"

Смотрите также

  • класс Prefetch,
    который используется для создания объекта предварительной загрузки
  • метод prefetch_related,
    который загружает связанные объекты отдельным запросом
  • метод select_related,
    который загружает связанные объекты через JOIN запрос
  • функция prefetch_related_objects,
    которая позволяет предварительно загрузить объекты в уже существующий список