Использование related_name и Manager в view-функции
У меня есть модели в Django. Объявлен новый атрибут published у модели Post и присвоен PostManager.
from core.models import PublishedModel
from django.contrib.auth import get_user_model
from django.db import models
from django.utils import timezone
User = get_user_model()
class Category(PublishedModel):
title = models.CharField(
'Заголовок',
max_length=256,
blank=False
)
description = models.TextField(
verbose_name='Описание',
blank=False
)
slug = models.SlugField(
'Идентификатор',
max_length=64,
unique=True,
help_text='Идентификатор страницы для URL; '
'разрешены символы латиницы, '
'цифры, дефис и подчёркивание.'
)
class Meta:
verbose_name = 'категория'
verbose_name_plural = 'Категории'
def __str__(self):
return self.title
class Location(PublishedModel):
name = models.CharField(
'Название места',
max_length=256,
blank=False
)
class Meta:
verbose_name = 'местоположение'
verbose_name_plural = 'Местоположения'
def __str__(self):
return self.name21
class PostQuerySet(models.QuerySet):
def published(self):
return self.filter(
is_published=True,
pub_date__date__lte=timezone.now(),
category__is_published=True
)
class PublishedPostManager(models.Manager):
def published(self):
return PostQuerySet(self.model, using=self._db).published()
class Post(PublishedModel):
objects = models.Manager()
published = PublishedPostManager()
title = models.CharField(
'Заголовок',
max_length=256,
blank=False
)
text = models.TextField('Текст', blank=False)
pub_date = models.DateTimeField(
'Дата и время публикации',
auto_now_add=False,
help_text='Если установить дату и '
'время в будущем — можно делать '
'отложенные публикации.'
)
author = models.ForeignKey(
User,
verbose_name='Автор публикации',
on_delete=models.CASCADE,
blank=False
)
category = models.ForeignKey(
Category,
on_delete=models.SET_NULL,
related_name='posts',
verbose_name='Категория',
null=True,
blank=False
)
location = models.ForeignKey(
Location,
on_delete=models.SET_NULL,
related_name='posts',
verbose_name='Местоположение',
null=True
)
class Meta:
verbose_name = 'публикация'
verbose_name_plural = 'Публикации'
def __str__(self):
return self.title
views.py
from django.conf import settings
from django.shortcuts import get_object_or_404, render
from django.utils import timezone
from .models import Category, Post
def index(request):
template = 'blog/index.html'
post_list = (
Post.published.published()
.order_by('-pub_date')
[:settings.POSTS_ON_PAGE]
)
context = {'post_list': post_list}
return render(request, template, context)
def post_detail(request, id):
template = 'blog/detail.html'
post = get_object_or_404(
Post.published.published()
| Post.objects
.filter(author_id=request.user.id),
id=id
)
context = {'post': post}
return render(request, template, context)
def category_posts(request, category_slug):
template = 'blog/category.html'
category = get_object_or_404(
Category,
slug=category_slug,
is_published=True
)
posts = category.posts.filter(
is_published=True,
pub_date__lte=timezone.now()
)
context = {'category': category, 'post_list': posts}
return render(request, template, context)
Код работает, но есть комментарии ревьюера:
post_list = (Post.published.published()
Нам не нужно звать тут метод published(), его уже вызывает менеджер.posts = category.posts.filter(
Когда мы используем related_name, мы можем указать менеджер, который хотим использовать, это позволит нам переиспользовать методы, это позволит избежать сортировок руками. category.posts(manager='')
При замене:
post_list = (
Post.published
.order_by('-pub_date')
[:settings.POSTS_ON_PAGE]
)
...
post = get_object_or_404(
Post.published
| Post.objects
.filter(author_id=request.user.id),
id=id
)
...
posts = category.posts(manager='published')
Условия из метода published() не выполняются. Не могу разобраться, как правильно работать с related_name и Manager в view-функции. Чтение документации ситуацию не прояснило.
- Переопределен метод published.
class PostQueryset(models.QuerySet):
def published(self):
return self.filter(
is_published=True,
pub_date__lt=timezone.now(),
category__is_published=True
).select_related('author', 'category', 'location')
class PublishedPostManager(models.Manager):
def get_queryset(self):
return PostQueryset(self.model, using=self._db).published()
class Post(PublishedModel):
objects = PostQueryset.as_manager()
published = PublishedPostManager()
- Вызов метода:
def index(request):
template_name = 'blog/index.html'
post_list = (
Post
.published
.order_by('-pub_date')
[:settings.POSTS_ON_PAGE]
)
context = {
'post_list': post_list
}
return render(request, template_name, context)
def post_detail(request, id):
template_name = 'blog/detail.html'
post = get_object_or_404(
Post.published, pk=id
)
context = {
'post': post,
}
return render(request, template_name, context)
def category_posts(request, category_slug):
template = 'blog/category.html'
category = get_object_or_404(
Category,
slug=category_slug,
is_published=True
)
posts = category.posts(manager='published').all()
context = {'category': category, 'post_list': posts}
return render(request, template, context)
Главная проблема была в определении метода published.
Добавлен метод select_related() - который позволяет получать связанные объекты из базы данных вместе с основным объектом.
Переопределяется метод objects = PostQueryset.as_manager()
QuerySet.as_manager() может использоваться для создания экземпляра Manager с копией пользовательских методов QuerySet:
class Post(PublishedModel):
objects = PostQueryset.as_manager()
Экземпляр Manager, созданный QuerySet.as_manager(), будет практически идентичен PublishedPostManager.
- related_name - создаваемое Django имя для обратной связи от модели Category к Post. related_name используется для создания более осмысленного и удобного имени для обратной связи между моделями.
category.posts(manager='<метод>')
posts = category.posts(manager='published').all()