Рассуждаем про декораторы
Каждый из нас не раз использовал декоратор 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 setted"
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_anonymouse(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 setted"
assert not message is None, "Message should be setted"
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_anonymouse = partial(redirect_message_decorator,
condition_func=is_anonymous)
И использование данного декоратора:
@should_be_anonymouse(message=u'Вам туда нельзя',
redirect_url=reverse_lazy('office'))
def some_view(request, *args, **kwargs):
pass
Заключение
Для использование декоратора с CBV можно использовать этот сниппет
Статья не очень сложная, но для новичков, надеюсь, она будет интересна.
Всем приятного изучения/использования/разработки чудесного фреймворка django.
Автор: Zapix