Комментарии 6
"Ничего ты не знаешь, Джон Сноу". Джордж Мартин, Буря мечей.
Вроде бы автор и выполнил доскональный разбор кода Django GCBV, но вот так ничего и не понял. А понимание приходит, когда Видами-Базирующимися-На-Классах пользуешься ежедневно несколько лет.
Я уважаю и ценю эту технологию Django из-за ее элегантности пользования и удобства работы с данными в процессе формирования тела response.
Теперь к статье:
Диспетчер URL Django всегда вызывает функцию которая должна вернуть response. В случае найденного совпадения - это будет функция добавляющая в response что-то, иначе это будет функция создающая 404 response, если не сказано другого.
Эти ВСЕ функции называются видами (Views).
Внутри видовой функции, может быть вызвана любая цепочка функций в последовательном или рекурсивном стиле:
func(*args, **kwargs); func1(*args, **kwargs); ....
func(*args, **kwargs): return func1(*args, **kwargs)....
Чехарда с локальными переменными в случае нескольких функций приводит к идее использования контейнера для хранения всех необходимых данных. Эту роль в GCBV выполняет видовый класс. Он прокидывается по всем вызовам функций первым аргументом, если не сказано иное.
По главам.
Глава 0. URL dispatcher
Обьявляем класс
class MyFrmView(FormView):
pass # it should be my class body
myfrmview = MyFrmView.as_view() # add alias
Как бы выглядел urlpatterns
на основе привычного Function-based View (FBV) и на Class-based View (CBV):
urlpatterns = [
# Function-based View
path('aboutFunc/', myfuncview),
# Class-based View
path('aboutView/', myfrmview)
]
Упс. Я что то делаю не так? Увы все так, реальный пример, когда urlpatterns формируется автоматически только для тех классов у которых в файле прописан превдоним для .as_view()
Глава 1. Class View.
Исторически сложилось, что в диспетчере можно задать специальные декораторы для видовой функции и доп аргументы предназначенные для конфигурирования функции.
согласно django.core.handlers.base для
path('myurl/', view_func, {**initkwargs}, name='updmodel')
вызов диспетчером URL выглядит так:
if url.dispatch == True:
ready_view_func = view_function_configurator(**initkwargs)
return ready_view_func(request, *args, **kwargs).
В итоге, зная как работает Python, пишем следующее:
class myfrmview(FormView):
def __call__(self, request, *args, **kwargs):
self.setup(request, *args, **kwargs)
return self.dispatch(request, *args, **kwargs)
И тогда в диспетчере URL больше не надо этих отвратительных .as_view():
path('myurl/', myfrmview, name='updmodel')
диспетчер инициирует класс, и вызовет метод __call __.
Увы эта полезная доработка ждет своего часа на полках bugtracing django core, поскольку она ломает в легаси проектах, где декорирование функции указано прямо в url. Но это стало поправимо с появлением method_decorator в Django.
# раньше
path('myurl/', csrf_exempt(myOldfrmview.as_view()), name='updmodel')
# теперь
path('myurl/', method_decorator(csrf_exempt, name='call')(myfrmview), name='myname')
в итоге родной as_view не будет вызываться никогда, вызов будет работать быстрее. и это хорошо.
остальные главы. не буду продолжать комментировать, поскольку все, что написано в статье, это подтверждение функциональности кода Django, и перевод текстов с сайта документации и других сайтов, без осмысления почему это так, зачем это так и как можно сделать лучше.
Если автор и начал рассказывать про GCBV, то совсем не сказал зачем они, об их пользе. Cложность и простота GCBV скрываются в осознанном их применении, до которого еще надо дойти.
Идеология GCBV - это совсем не про код модулей django.views.generic, это, в первую очередь, про скорость разработки c неполным следованием в коде принципам SOLID/DRY.
Скорость выигрывается за счет того, что имея набор модулей из django.views.generic я могу создать 99% всех необходимых обработчиков запросов для проекта без написания кода и только обьявлением и конфигурацией видовых классов.
Скорость выигрывается еще за счет того, что это все уже протестировано.
Скорость выигрывается еще и за счет того, что архитектура цепочки вызовов более-менее оптимизирована.
В итоге с GCBV кодовая база проекта получается микроскопической, понятной и простой. И это хорошо!
Автору успехов, видно что он только в самом начале своего творческого пути.
Вы абсолютно правы! У меня нет вашего опыта и я сейчас на самом старте изучения Django, эта статья как раз и есть результат той боли новичка, который сталкивается с куцыми инструкциями в курсах и мануалах, но не понимает, как они работают и что под собой несут.
Правда, после прочтения вашего коментария, я так и не нашёл каких-то фундаментальных опровержений моего изложения, только дополнения, за что вам, конечно же, спасибо.
Увы эта полезная доработка ждет своего часа на полках bugtracing django core, поскольку она ломает в легаси проектах,
Я знаю иную историю. Если view сохраняет какое-то промежуточное состояние в свои свойства, то создание единственного экземпляра такого объекта непосредственно в urls.py может быть чревато появлением не особо тривиальных в отладке багов, из-за того, что состояние из одного запроса будет протекать в следующий. Другой причиной было соглашение избегать использования магических методов python в публичных интерфейсах фреймворка. Поэтому вместо варианта __call__ сделали фабричный метод as_view, который на каждый запрос создает новый экземпляр объекта.
Вариант, что не хотят __call __ использовать, подходит больше всего, как объяснение. Это объясняет появление setup вместо __init __.
Разумеется, что:
def __call__(self, request, *args, **kwargs):
# забыл инстанс создать
self = type('MyCurrentCall', (type(self),), vars(self))()
self.setup(request, *args, **kwargs)
return self.dispatch(request, *args, **kwargs)
Иначе получится админ панель Django, где именно синглтоны использованы.
А ещё as_view(**initargs)
принимает параметры для кастомизации эндпоинта. Получается что-то в роде partial application для view-функции. Просто в то время в трендах было ООП, наследование и все такое.
Как работают Django Class-based views