Comments 14
except Category.DoesNotExist:
Если из контекста нельзя получить класс модели то можно делать:
from django.core.exceptions import ObjectDoesNotExist
В самом последнем примере
queryset = User.objects.all()
лучше поправить на
manager = User.objects
и по месту исправить queryset на manager
Причина в том, что результат .all() — кешируется и может привести к неожиданным ошибкам.
Не совсем понятно, почему не через видовые классы решается, кода еще меньше и почему не используется repath для предобработки.
Но, так или иначе, пример именно с User-репортом не совсем корректный.
Предположу, что нам нужен лист записей о действии юзера и, что данные об этом хранятся в отдельной модели UserReport.
Тогда для получения Repor-Листа получать обьект User нам не надо — можно сразу фильтровать по числовому значению user_id. Нет записей — возвращаем «Записей по запрошенному пользователю не найдено».
Конечно, когда надо делать что-то с самим пользователем, то можно получить связанный объект User, например, одновременно с запросом по UserReport.
Если придерживаться идеологии джанго, то Resolver должен работать максимально быстро, и, желательно, без запросов в БД: в resolver нет текущего пользователя, который прикреплен в request, и не известно, имеет ли текущий пользователь права доступа к модели обработчика, в примере это модель Users.
Обработка даты, idsn, EAN — да, это возможно. Как я понимаю в Djangoproject просто отдали на откуп создание частных обработчиков, оставив себе общие случаи: path, slug, string, int, UUID.
.all()
кеширует результаты при исполнении, а как раз в приведенном конвертере исполняется не .all()
, а .get()
, поэтому проблем не возникнет. При этом получаем возможность в декларативном стиле добавлять фильтрации:
queryset = User.objects.filter(is_active=True)
Если же захочется в конвертере использовать
.all()
, то тогда в нем же и стоит позаботиться об актуальности данных :)def to_python(self, value: str):
# ensure queryset is re-evaluated
queryset = self.queryset.all()
# process queryset
Но пока не могу представить себе конвертер, в котором это потребовалось бы.
Про видовые классы и
re_path
не понял. Финальная view-функция выглядит так:path('users/<user:u>/reports/<date:dt>/', user_report, name='user_report')
def user_report(request, u: User, dt: datetime):
# logic
CBV выглядел бы так:
path('users/<user:u>/reports/<date:dt>/', UserReport.as_view(), name='user_report')
class UserReport(generic.View):
def get(self, request, u: User, dt: datetime):
# logic
Можно пояснение, что имелось ввиду?
Что же касается логики получения самого отчета — действительно, возможно нужны будут связанные модели и сам
User
не нужен вовсе, а возможно улетит запрос в сторонний сервис и нужен будет некий токен из модели User
. Тут уже зависит от задач — если обработчик использует user_id
— значит его и получаем, если же использует User
, то хотелось бы получить User
на вход.А вот насчет отсутствия
request
согласен. Сделать запрос в БД, чтобы убедиться в валидности маршрута, а потом узнать что нет прав на исполнение этого обработчика и можно было бы не делать запрос в БД — грустный сценарий. Это будет бить по производительности.При этом разрешать вопрос прав нужно не в конвертере или
URLResolver
, это не их забота, а частично до (вот этого слоя не хватает, простые проверки, типа маршрут только для админа) и частично после (в обработчике).На практике же, действительно, пока из самописных конвертеров я использую только конвертер для даты :)
Интересная штука, но мне кажется, дальше чем даты или какие-то простые скалярные конвертеры, использовать не стоит.
Пример с юзером — хорош для демонстрации, но на практике скорее всего приведёт к куче лишних запросов к БД. И главное, как мне кажется, это размывание логики — вставка модельной логики в декларативные УРЛы и вынос важной части обработки собственно запроса в другое место. Как только в этой части, считающейся декларативной, возникает сайд эффект — возникают проблемы.
То же, что и с сигналами — вещь в теории полезная, на практике приводящая к большому батхёрту, если не сдерживаться и не пичкать модели комментариями "смотри сигналы".
А размывание логики не стоит допускать — задача конвертера только дать ответ: могу сконвертировать или нет = маршрут не подходит, сайд эффекты привносить в конвертер точно не стоит :)
Я к конвертерам скорее отношусь как к сериализаторам из DRF, только которые принудительно вызываются до обработчика, а не внутри.
С другой стороны, если сделать общую вьюху (для всех этих типов страниц) — и там по slug определять тип страницы, то получаем при этом точно такие же лишние запросы к БД. Так что в такой ситуации самый правильный вариант, на мой взгляд, это пытаться по возможности избежать такой структуры URL-адресов. То есть сделать test.com/cars/car-slug, test.com/dogs/dog-slug и test.com/posts/post-slug. Тогда не нужно лазать в БД ради роутинга.
Если приходится для роутинга лезть в БД, то тут как ни крути — придется лезть в БД. Кажется, что в обработчике будет меньше запросов (если сам объект знает все что нужно, чтобы выбрать нужный роут).
А что не так с reverse в случае вьюхи? Делаю в своих проектах именно вьюхи, проблем вроде не испытываю
то как reverse будет работать?
Эм, а почему он должен не работать?
class Auto(models.Model):
def get_absolute_url(self):
return reverse("common_url", kwargs={"slug": self.slug})
class Article(models.Model):
def get_absolute_url(self):
return reverse("common_url", kwargs={"slug": self.slug})
Пишу в своих проектах подобный код — всё отлично работает
Конвертеры маршрутов в Django 2.0+ (path converters)