Топ 6 декораторов в Django

Что такое декоратор?

Декоратор — это название одного из самых популярных шаблонов проектирования, используемых в наше время, и мы часто пользуемся им, не зная, что мы используем шаблон проектирования. И что же такого особенного в этом шаблоне? Как мы можем прочитать на Python Wiki, используя этот шаблон, мы получаем способ явно изменить поведение объекта, заключив его в украшающий объект с похожим интерфейсом.

Почему стоит использовать декораторы в своем веб-приложении?

Декораторы динамически изменяют поведение функции, метода или класса без необходимости создавать подклассы или изменять исходный код декорированного класса. Благодаря этому наш код станет более чистым, более читаемым, поддерживаемым (что немаловажно) и сократит стандартный код, позволив нам добавлять функциональность к нескольким классам, используя один метод.

Хороший пример важности и простоты использования этих декораторов можно увидеть в декораторе @login_required, который предоставляет django, и который вы, вероятно, использовали, если у вас есть некоторый опыт работы с нашим любимым фреймворком. Это просто кусок кода, где мы проверяем, не прошла ли аутентификация пользователя, и тогда он перенаправляется на URL-адрес входа.

Способ использования декораторов следующий:

from django.contrib.auth.decorators import login_required


@login_required
def my_view(request)
    …

Каждый раз, когда пользователь пытается получить доступ к my_view, код внутри login_required будет использоваться.

Несколько наших любимых декораторов

В этом разделе мы покажем вам некоторые из декораторов, которые мы считаем наиболее полезными или которые мы когда-либо использовали с успехом. Помните, что многие из них можно настроить в соответствии с вашими потребностями.

Group Required

Иногда нам нужно защитить некоторые вьюшки, чтобы позволить определенной группе пользователей получить к ней доступ. Вместо того, чтобы проверять в этой вьюшке, принадлежит ли пользователь к этой группе/группам, мы можем использовать следующий декоратор.

from django.contrib.auth.decorators import user_passes_test


def group_required(*group_names):
   """Requires user membership in at least one of the groups passed in."""

   def in_groups(u):
       if u.is_authenticated():
           if bool(u.groups.filter(name__in=group_names)) | u.is_superuser:
               return True
       return False
   return user_passes_test(in_groups)


# The way to use this decorator is:
@group_required(‘admins’, ‘seller’)
def my_view(request, pk)
    ...

Больше информации можно найти тут.

Anonymous required

Этот декоратор основан на декораторе login_required Django, но работает противоположно, и проверяет, является ли пользователь анонимным, в противном случае пользователь перенаправляется на веб-сайт, определенный в нашем файле settings.py, и может быть полезен, когда мы хотим защитить пользовательские вьюшки, такие как вход в систему или регистрация.

def anonymous_required(function=None, redirect_url=None):

   if not redirect_url:
       redirect_url = settings.LOGIN_REDIRECT_URL

   actual_decorator = user_passes_test(
       lambda u: u.is_anonymous(),
       login_url=redirect_url
   )

   if function:
       return actual_decorator(function)
   return actual_decorator


# The way to use this decorator is:
@anonymous_required
def my_view(request, pk)
    ...

Дополнительная информация по ссылке.

Superuser required

Это тот же случай, когда мы хотим разрешить определенным группам доступ к вьюхе, но в этом случае только суперпользователи могут посетить её.

from django.core.exceptions import PermissionDenied


def superuser_only(function):
  """Limit view to superusers only."""

   def _inner(request, *args, **kwargs):
       if not request.user.is_superuser:
           raise PermissionDenied           
       return function(request, *args, **kwargs)
   return _inner


# The way to use this decorator is:
@superuser_only
def my_view(request):
    ...

Информация лежит тут.

Ajax required

Этот декоратор проверяет, является ли запрос AJAX-запросом, этот декоратор полезен, когда мы работаем с Javascript-фреймворками такими как jQuery, и является хорошим способом защиты нашего приложения.

from django.http import HttpResponseBadRequest


def ajax_required(f):
   """
   AJAX request required decorator
   use it in your views:

   @ajax_required
   def my_view(request):
       ....

   """   

   def wrap(request, *args, **kwargs):
       if not request.is_ajax():
           return HttpResponseBadRequest()
       return f(request, *args, **kwargs)

   wrap.__doc__=f.__doc__
   wrap.__name__=f.__name__
   return wrap


# The way to use this decorator is:
@ajax_required
def my_view(request):
    ...

Немного больше информации как всегда по ссылке.

Time it

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

def timeit(method):

   def timed(*args, **kw):
       ts = time.time()
       result = method(*args, **kw)
       te = time.time()
       print('%r (%r, %r) %2.2f sec' % (method.__name__, args, kw, te - ts))
       return result

   return timed


# The way to use this decorator is:
@timeit
def my_view(request):
    ...

Получить дополнительные знания по этому поводу можно здесь.

Кастомная функциональность

Следующий декоратор — это всего лишь пример того, как вы можете проверить некоторые разрешения совсем просто и на 100% настроить это так, как вам необходимо.

Представьте, что у вас есть блог, магазин, форум… Если у пользователей должно быть несколько очков активности, чтобы написать отзыв, это стало бы хорошим способом избежать спама. Мы создадим декоратор, чтобы проверить, что пользователь вошел в систему и имеет более 10 баллов, поэтому может написать отзыв, в противном случае мы не дадим ему сделать этого.

from django.http import HttpResponseForbidden


logger = logging.getLogger(__name__)


def user_can_write_a_review(func):
   """View decorator that checks a user is allowed to write a review, in negative case the decorator return Forbidden"""

   @functools.wraps(func)
   def wrapper(request, *args, **kwargs):
       if request.user.is_authenticated() and request.user.points < 10:
           logger.warning('The {} user has tried to write a review, but does not have enough points to do so'.format( request.user.pk))
           return HttpResponseForbidden()

       return func(request, *args, **kwargs)

   return wrapper

Итак, мы рассмотрели несколько интересных декораторов для Django и даже написали свой собственный. От себя лишь можем добавить, что знание декораторов поможет вам писать код во много раз эффективнее.