作者👨💻:Pablo Iglesias
时间🕐:2018/06/27
原文链接🌐:https://www.bedjango.com/blog/top-6-django-decorators/
声明👉:本文为转载,仅作为个人学习记录
What is a Decorator?
A decorator is the name of one of the most popular design patterns used nowadays, many times we use it without knowing that we are using a design pattern. And what’s so special about this pattern? As we can read at Python Wiki using It is a way of apparently modifying an object’s behavior, by enclosing it inside a decorating object with a similar interface. You can get more information about Pattern Design here.
Why I should use decorators in my web application?
Decorators dynamically alter the functionality of a function, method or class without having to make subclasses or change the source code of the decorated class. Thanks to this our code will be more cleaner, more readable, maintainable (Which is no small thing), and reduce the boilerplate code allowing us to add functionality to multiple classes using a single method. A good example of the importance and easy of use of these decorators can be seen in the decorator @login_required that provides django, and that you probably used if you have some experience with our favorite framework. It’s just a piece of code where we check if the user is not authenticated the user is redirected to the login url.
The way that the decorators as used is the following:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request)
…
Each time that a user try to access to my_view, the code inside login_required will be ejecuted.
Some of our favorite decorators
In this section we will show you some of the decorators that we think are most useful or that we have ever used with positive results, keep in mind that many of these can be customized to suit your needs. For this post we will use the original decorators with their font.
Group Required
Sometimes we need to protect some views, to allow a certain group of users to access it. Instead of checking within it if the user belongs to that group/s, we can use the following decorator
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)
...
You can get more information about it here
Anonymous required
This decorator is based on the decorator login_required of Django, but looks for the opposite case, that the user is anonymous, otherwise the user is redirected to the website defined in our settings.py and can be useful when we want to protect logged user views, such as the login or registration view
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)
...
You can get more information about it here
Superuser required
This is the same case as when we want to allow certain groups access to a view, but in this case only super users can visit it.
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):
...
You can get more information about it here
Ajax required
This decorator check if the request is an AJAX request, useful decorator when we are working with Javascript frameworks as jQuery and a good way to try to secure our application
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):
...
You can get more information about it here
Time it
This decorator is very helpful if you need to improve the response time of one of then our views or if you just want to know how long it takes to run.
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):
...
You can get more information about it here
Custom Functionality
The next decorator is just an example about how you can check some permissions or some checks in a easy way and 100% customizable.
Imagine you have a blog, shop, forum…. Where users need to have a number of points in order to write a review, it would be a good way to avoid SPAM for example. We’ll create a decorator to check that the user is logged in and has more than 10 points, so you could write a review, otherwise we’ll raise a Forbidden
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
You can get more information about it asking myself at piglesias@emergya.com or via twitter @pypiglesias. I really hope you found the post interesting or at least curious. From BeDjango we would like to encourage you to share your decorators, ideas or questions as well as interesting topics for future posts