Рассуждаем про декораторы
Каждый из нас не раз использовал декоратор login_required и скорее всего писал похожий декоратор(скажем для проверки пустая ли корзина). Давайте рассмотрим что делает данный декоратор:

Если посмотреть на это целиком то можно представить так:

Декоратор, который проверяет пришедший к нему запрос и в зависимости от результата отдает либо view, либо другую функцию — назовем декоратором проверки.
Так как схема одна и та же, мы можем написать «заготовку» для декоратора:
def check_decorator(view=None, condition_func = lambda request, *args, **kwargs: True, false_func = lambda request, *args, **kwargs: HttpResponse()): ''' Checks by :function:condition_func, if true go to :function: view, else go to :function:false_func :param condition_func: Should return Boolean value :type condition_func: function :param false_func: Should return object of :class:`http.HttpResponse' :return: :class:`http.HttpResponse` ''' def decorator(view): @wraps(view) def wrapper(request, *args, **kwargs): if not condition_func(request, *args, **kwargs): return false_func(request, *args, **kwargs) return view(request, *args, **kwargs) return wrapper return decorator(view) if view else decorator
check_decorator — это базовая функция, которая принимает view, функцию условия(condition_func) и функцию, которая используется при отрицательном результате условия(false_func).
Теперь что бы создать функцию, которая авторизованных пользователей выкидывает в личный кабинет(предположим с регистрации) нам следует сделать следующие шаги:
- Создать функции проверки is_anonymous
- Создать функцию, которая будет перекидывать в офис(office_redirect)
- C помощью functools.partial и наших функций(is_anonymous, office_redirect) создадим декоратор
from functools import partial is_anonymous = lambda request, *args, **kwargs: request.user.is_anonymous() office_redirect = lambda request, *args, **kwargs: redirect('office') # office - имя view в urls should_be_anonymous = partial(check_decorator, condition_func=is_anonymous, false_func=office_redirect)
Думаю, что написать другую функцию условия или функцию, которая вызывается при не пройденном условии(false_func), у вас не составит труда.
А как же без redirect_url
Многие из нас привыкли, что можно указать куда будем пересылать пользователя, указывая redirect_url:
@login_required(redirect_url=reverse_lazy('my_view_name')) def some_view(request, *args, **kwargs): pass
В данном случае у нас функция false_func всегда производит redirect, а мы должны указывать куда производить url.
Создадим данную функцию на основе нашей «заготовки»:
def redirect_decorator(view=None, redirect_url=None, **kwargs): ''' Uses :function:`check_decorator` with false_func that returns :param:`redirect_url` ''' assert not redirect_url is None, "Redirect_url should be set" if 'false_handle_func' in kwargs: del kwargs['false_func'] redirect_func = lambda request, *args, **kwargs: redirect(redirect_url) return check_decorator(view=view, false_func=redirect_func, **kwargs)
В функции redirect_decorator мы проверяем передан ли нам redirect_url, и если нам передали
false_func — удаляем её. Затем возвращаем значение функции check_decorator.
Функцию should_be_anonymous можем переписать так:
should_be_anonymous = partial(redirect_decorator, condition_func=is_anonymous)
И использовать в привычной нам форме:
@should_be_anonymous(redirect_url=reverse_lazy('office')) def some_view(request, *args, **kwargs): pass
Передадим сообщение
В django есть замечательный механизм сообщений django.messages, при помощи которого мы можем передать сообщения нашему пользователю.
Посмотрим как он работает:
Пишем во view:
from django.contrib import messages def view(request, *args, **kwargs): ... messages.info("Hello World!") #Передаем сообщение польpователю ...
Показываем в шаблоне:
{% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %}
При помощи данного механизма мы можем уведомить пользователя, почему он не попал на какую-то страницу.
Создадим функцию, которая будет производить редирект и передавать наше сообщение:
def redirect_message_decorator(view=None, message=None, redirect_url=None, **kwargs): ''' Uses :function: c`check_decorator` with false_func thar returns :param:`redirect_url` ''' assert not redirect_url is None, "Redirect_url should be set" assert not message is None, "Message should be set" if 'false_func' in kwargs: del kwargs['false_func'] def redirect_func(request, *args, **kwargs): messages.info(request, message) return redirect(redirect_url) return check_decorator(view=view, false_func=redirect_func, **kwargs)
Функция should_be_anonymous с сообщением выглядит так:
should_be_anonymous = partial(redirect_message_decorator, condition_func=is_anonymous)
И использование данного декоратора:
@should_be_anonymous(message=u'Вам туда нельзя', redirect_url=reverse_lazy('office')) def some_view(request, *args, **kwargs): pass
Заключение
Для использование декоратора с CBV можно использовать этот сниппет
Статья не очень сложная, но для новичков, надеюсь, она будет интересна.
Всем приятного изучения/использования/разработки чудесного фреймворка django.
