Pull to refresh

Comments 14

def form_valid(self, form):
        Post.objects.create(**form.cleaned_data)
        return redirect(self.get_success_url())

Это жесть и чревато ошибками. Лучше так:
def form_valid(self, form):
        # Мы используем ModelForm, а его метод save() возвращает инстанс
        # модели, связанный с формой. Аргумент commit=False говорит о том, что
        # записывать модель в базу рановато.
        instance = form.save(commit=False)

        # Теперь, когда у нас есть несохранённая модель, можно ей чего-нибудь
        # накрутить. Например, заполнить внешний ключ на auth.User. У нас же
        # блог, а не анонимный имижборд, правда?
        instance.user = request.user

        # А теперь можно сохранить в базу
        instance.save() 

        return redirect(self.get_success_url())


И ещё: не надоело в каждой вьюхе писать что-то такое?
    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        super(CreatePost, self).dispatch(request, *args, **kwargs)


Особенно нелепо это выглядит на фоне class based, основная идея которого — избавить нас от рутины. Почему бы не сделать миксин, в котором все лишь раз прописать эту магическую формулу, а потом уже наследовать все вьюхи от него?
Это жесть и чревато ошибками. Лучше так

Ну в принципе я указал, что все примеры синтетические. В любом случае перед сохранением объекта потребуются дополнительные проверки или необходимо проставить внешние ключи, о чем вы уже упомянули. Не хотелось просто перегружать примеры логикой, которая не связанна непосредственно с ними. Спасибо за пример, добавлю и его как демонстрация более правильного решения данной задачи.

И ещё: не надоело в каждой вьюхе писать что-то такое?

Как я выше упомянул — примеры надуманные и не связанные между собой. Это не часть какого-то проекта, я просто упомянул о существовании данной возможности

Разумеется если вам потребуются соответсвующие проверки на наличие авторизации у пользователей или соответствующих прав, то вы можете сделать это используя декораторы для метода dispatch или добавить свою логику проверки:


Спасибо еще раз за объективную критику, на мой взгляд многие из ваших комментариев были полезнее самой статьи)
Ну как минимум миксинов будет не один, а пачка.
В реальном проекте часто бывает нужен не один, а два-три декоратора в разном сочетании.
Ну и для передачи параметров для миксинов придется писать код в любом случае.
Давайте всё же попробуем обойтись одним миксином безо всяких пачек? =)

class DecoratorChainingMixin(object):
    def dispatch(self, *args, **kwargs):
        decorators = getattr(self, 'decorators', [])
        base = super(DecoratorChainingMixin, self).dispatch

        for decorator in decorators:
            base = decorator(base)
        return base(*args, **kwargs)

А потом нам потребуется всего лишь перечислить требуемые декораторы в нужном порядке:

class MyView(DecoratorChainingMixin, UpdateItem):
    form_class = ItemForm
    model = Item
    decorators = [login_required, never_cache, i_cherta_lysogo_v_stupe(foo='bar')]


Но это, что называется, низкий уровень. Если хочется абстракций, можно пытаться писать микшины с изменяемым поведением:

class UserPassesTestMixin(object):
    def user_passes_test(self, user):
        return user.is_authenticated()

    def user_failed_test(self):
        return redirect(LOGIN_URL)

    def dispatch(self, request, *args, **kwargs):
        if not self.user_passes_test(request.user):
            return self.user_failed_test()
        return super(UserPassesTestMixin, self).dispatch(request, *args, **kwargs)

Наследуемся и когда потребуется сменить логику проверки доступа пользователя, просто перекрываем user_passes_test. В том маловероятном случае, если не устраивает редирект на страницу логина, можно перекрыть и user_failed_test.
Это уже интереснее. Спасибо!
Попробую первый вариант использовать в текущем проекте :)
Нужно пнуть разработчиков django чтобы улучшили работу с декораторами.
Да и вообще текущая документация по CBV очень куцая…
В стиле django это должно называться иначе:
-class DecoratorChainingMixin(object):
+class ClassBasedViewMiddleware(object):
Почему? Это же не миддльварь всё-таки, а именно микшин, хоть и странный.
login_required можно оборачивать в urls.py.

url(r'^/register$', login_required(AnyView.as_view())),
Это не всегда подходит. А так да, можно.
Оно может не подходить если в хендлере необходима кастомная проверка (но в этом случае декоратор вообще не подходит), а в целом оборачивание метода dispatch делает то же самое.
а это конец или будет еще? Можно еще тему миксинов разжувать :)
Ну если кто-нибудь не возьмется написать, то возможно попробую собрать список используемых миксинов. Хотя тут все уже зависит от конкретных задач и универсальных рецептов нет.
В наборе стандартных CBV не хватает вьюх для обработки формсетов и работы с дочерними объектами (inline). В нескольких проектах требовался такой функционал. Еще пара подобных проектов и можно будет куда-нибудь выложить боле-менее универсальное решение.
Спасибо за цикл статей. Как-то потратил много времени, чтобы разобраться.
Sign up to leave a comment.

Articles