Preconditions ============= When writing views, a common situation is that you have some checks that need to be done at the beginning of a view before the main logic, and several views might share the same checks. If the check fails you might want to redirect the user to a different page, but other options are possible, such as displaying a message (perhaps using the `messages framework `_). Python “decorators” are a perfect match for these kind of things. If you haven't used decorators at all before, I'd recommend this `Primer on Python Decorators `_. If you just want to apply an existing decorator to a view, that's very easy, but a good understanding of what is going on is necessary if you want to be able to implement them. Plus, you'll get a huge amount of benefit in other ways from this very general Python technique. First let's look at our starting point. We have a page that should only be accessible to 'premium' users. If, somehow, a non-premium user gets the link to the page, they should be redirected to their account page, and also shown a message. It might look like this: .. code-block:: python def my_premium_page(request): if not request.user.is_premium: messages.info(request, "You need a premium account to access that page.") return HttpResponseRedirect(reverse('account')) return TemplateResponse(request, 'premium_page.html', {}) Now, we want to re-use those first 3 lines of logic. The neatest way is to put them in a decorator, which we will use like this: .. code-block:: python @premium_required def my_premium_page(request): return TemplateResponse(request, 'premium_page.html', {}) To understand how to implement a decorator, it's often useful to remember what decorator syntax is doing. The long-hand way of defining ``my_premium_page``, equivalent to the above, is like this: .. code-block:: python def my_premium_page(request): return TemplateResponse(request, 'premium_page.html', {}) my_premium_page = premium_required(my_premium_page) In other words, ``premium_required`` is a function that takes a view function as input, and returns a new, replacement view function as output. The view function it returns will **wrap** the original view function. In our case, it will also add some additional checks and logic, and in some cases (where the user is not a premium user), it will decide to bypass the original view function and return its own response. So the implementation of ``premium_required`` will look like this: .. code-block:: python import functools def premium_required(view_func): @functools.wraps(view_func) def wrapper(request, *args, **kwargs): if not request.user.is_premium: messages.info(request, "You need a premium account to access that page.") return HttpResponseRedirect(reverse('account')) return view_func(request, *args, **kwargs) return wrapper The ``@functools.wraps(view_func)`` line may not be strictly necessary. But it makes our wrapper function view behave more nicely — for example, it copies the name and docstring of the original view over, along with other attributes. These make debugging nicer, and sometimes it can be important for functionality too (for instance, if you are wrapping something that has been wrapped in ``csrf_exempt``) — so you should always add it. So far, the views we're using it on only take a single ``request``, so making our wrapper take ``*args`` and ``**kwargs`` might not seem necessary. But we want this decorator to be generic and future proof, so we put those in there from the start. Adding multiple decorators -------------------------- Our decorator as above has an issue — if an anonymous user accesses it, ``request.user`` will be an ``AnonymousUser`` instance, and won't have an ``is_premium`` attribute, which will result in a 500 error. A nice way to tackle this is to use the Django-provided ``login_required`` decorator, which will redirect to the login page for anonymous users. We simply need to apply both decorators. The correct order is as follows: .. code-block:: python from django.contrib.auth.decorators import login_required @login_required @premium_required def my_premium_page(request): return TemplateResponse(request, 'premium_page.html', {}) The checks that ``login_required`` does ensure that by the time we get into the ``premium_required`` view wrapper, we are guaranteed to have a logged in user. Ordering multiple decorators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When dealing with multiple decorators, as above, ordering can be very important, and it's easy to get confused about what order everything is happening. The best analogy I know of is to think of it as an **onion**. In the centre, you have the actual view function, and each decorator adds a layer. Let's write it out the long hand way as a visualisation: .. code-block:: python def my_premium_page(request): return TemplateResponse(request, 'premium_page.html', {}) my_premium_page = \ login_required( premium_required( my_premium_page ) ) So, ``premium_required`` is the **innermost** decorator. It is the first to be **applied** to ``my_premium_page``, while ``login_required`` is the **outermost** decorator, and it is the last to be applied. **BUT!** The decorators themselves (the functions ``premium_required`` and ``login_required``) are distinct from the wrappers they return! So, the preconditions that the ``login_required`` wrapper adds are run **first** (because it is the outermost), and the preconditions that the ``premium_required`` wrapper adds are run **last** (because it is the innermost). The result is actually very intuitive — the preconditions added by each decorator are run in the order that the decorators appear in your source code. However, you might also want to do post-processing in your view wrappers. If you do that, remember the onion metaphor — post-processing from the innermost wrapper will run before post-processing from the outermost wrapper. Exercise ~~~~~~~~ If the above left you horribly confused, I think the best way to understand this is to get your hands dirty with some examples, so here is an exercise. Suppose we have the following decorators (which do nothing other than print some things): .. code-block:: python def decorator_1(view_func): print("In decorator_1") def wrapper(request, *args, **kwargs): print("In decorator_1 wrapper, pre-processing") response = view_func(request, *args, **kwargs) print("In decorator_1 wrapper, post-processing") return response return wrapper def decorator_2(view_func): print("In decorator_2") def wrapper(request, *args, **kwargs): print("In decorator_2 wrapper, pre-processing") response = view_func(request, *args, **kwargs) print("In decorator_2 wrapper, post-processing") return response return wrapper Then, what will the following code blocks print? .. code-block:: python >>> @decorator_1 ... @decorator_2 ... def my_view(request): ... print("In my_view") ... return "I am a response" .. code-block:: python >>> response = my_view(None) First make your guesses, then try the code from a Python prompt. If you get it wrong, have another look until you understand exactly what is going on. Hints: * Replace the ``@`` syntax with the long-hand version * Simplify using no decorators, then one decorator, then two decorators Combining multiple decorators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have multiple decorators that need to be applied in a certain order, or where you often have them together, you should probably be thinking about building a single decorator that combines them — for which I can do no better than point you to Adam Johnson's post `How to Combine Two Python Decorators `_! You could also see this Stackoverflow post with `general code for composing any number of decorators `_ Built-in decorators ------------------- Also, don't miss out on the decorators and "decorator factories" than come with Django and cover many of the common cases, such as ``login_required`` (already used), `user_passes_test `_ and `permission_required `_ Next up: :doc:`policies`. .. _mixins-do-not-compose: Discussion: Mixins do not compose --------------------------------- Django also provides mixins for applying preconditions, like `LoginRequired `_ etc., which work by overriding the ``dispatch()`` method. Now, suppose we were to go the CBV route, and have a ``PremiumRequired`` mixin instead of ``@premium_required``. Let's also add another similar check — ``GoodReputationRequired`` which does some kind of reputation check (perhaps this is a social site with moderation in place). To require a user to have both, is it enough to just add both mixins? Similarly, could I produce a new mixin like this? .. code-block:: python class PremiumAndGoodReputationRequired(PremiumRequired, GoodReputationRequired): pass The answer is: **it depends**. Let's suppose we used the `UserPassesTestMixin `_ that Django provides, which will make our mixins quite short and simple. In this case, our mixins will **not** compose as required, but will **silently fail** — only one of the checks will run. If this was a feature critical for security, that could be rather bad! But if we implemented our base mixins some other way (like only overriding ``dispatch()``, and using ``super()`` correctly), they **should** compose. (See the `preconditions discussion_views.py `_ file for a full example of both) This issue is noted in the docs for ``UserPassesTestMixin`` — you cannot stack multiple uses of it. However, docs or not, this is a great example of how, in general, **mixins do not compose**. I've said it :ref:`before ` but it is worth repeating. You can have two mixins that work perfectly apart, but fail together. To be sure that they do compose, you have to **know the implementation**. Further, it's entirely possible that when you first put them together there is no issue, but a later change means they may start failing in the worst possible way — **silently**. This is a horrible way to write software! As much as possible, we should choose techniques where you can simply depend on the interface of some code to know that you are using it correctly, rather than its implementation. Decorators aren't prone to this problem. Mixins are like injecting things right into the middle of someone else's code, hoping that the context will fit, while decorators wrap existing functionality depending only on its interface.