Как стать автором
Обновить

Комментарии 14

У вас там баг в паре мест. Делаете query в одну модель а эксепшн от другой:
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, только которые принудительно вызываются до обработчика, а не внутри.

Именно так. :)


сериализаторам из DRF

удачное сравнение.


В общем спасибо за подкинутую идею, Today I learned как говорится.

Использовал конвертеры маршрутов, когда нужно было размещать страницы разных типов на одном уровне вложенности URL. То есть, грубо говоря такая ситуация: test.com/car-slug (страница с каким-то автомобилем), test.com/dog-slug (страница про какую-то собаку), test.com/post-slug (какая-то статья) — и во всех 3-х случаях разные шаблоны и переменные для них. Конвертеры маршрутов решили эту проблему, но, увы, делаются лишние запросы к базе данных (чтобы по slug определить тип страницы) — и это не очень хорошо.

С другой стороны, если сделать общую вьюху (для всех этих типов страниц) — и там по slug определять тип страницы, то получаем при этом точно такие же лишние запросы к БД. Так что в такой ситуации самый правильный вариант, на мой взгляд, это пытаться по возможности избежать такой структуры URL-адресов. То есть сделать test.com/cars/car-slug, test.com/dogs/dog-slug и test.com/posts/post-slug. Тогда не нужно лазать в БД ради роутинга.
Ого, честно, не подумал бы даже делать такое на конвертерах :)
Если приходится для роутинга лезть в БД, то тут как ни крути — придется лезть в БД. Кажется, что в обработчике будет меньше запросов (если сам объект знает все что нужно, чтобы выбрать нужный роут).
А там вариантов не сильно много: либо конвертер, либо вьюха. Я это сунул в конвертер ещё и для того, чтобы reverse урлов работал.

А что не так с reverse в случае вьюхи? Делаю в своих проектах именно вьюхи, проблем вроде не испытываю

Я это написал в контексте того комментария, который написал в самом начале. Если сделать общую вьюху под несколько страниц, внутри которой по части path будет разруливаться, какой шаблон рендерить и с какими переменными, то как 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})

Пишу в своих проектах подобный код — всё отлично работает

А, ну ок — значит, я ошибся
Зарегистрируйтесь на Хабре, чтобы оставить комментарий