Long story short:
-
Django lets you call functions in templates, but you can't pass any parameters.
-
Sometimes you need to use the request object to perform certain tasks, such as determining whether the current user has permission to do something.
-
The recommended approach is to call functions that require parameters in the view, and then pass the results as variables in the context. This sometimes feels a bit overkill.
-
Creating a templatetag to call any function with any parameter will definitely break the intention for not letting functions to be called with parameters in templates.
-
So, what if we could tell django to inject the request into certain functions? That's what this decorator is for.
For instance, suppose you have a model:
class SomeModel(models.Model):
...
def current_user_can_do_something(self, request):
...some logic here using request...
You could use the decorator like this:
class SomeModel(models.Model):
...
@inject_request
def current_user_can_do_something(self, request=None):
...some logic here using request...
And in the template go straight to:
{{ somemodel_instance.current_user_can_do_something }}
So that the decorator would perform some operations to find the request in the frame tree and inject it if found. The assertions are intented to make sure things will work even if the request cannot be found, so that the coder may program defensively.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # -*- coding: utf-8 -*-
from __future__ import unicode_literals
import inspect
from django.http import HttpRequest
from django.utils import six
def inject_request(func):
argspec = inspect.getargspec(func)
assert 'request' in argspec.args, "There must be a 'request' argument in the function."
index = argspec.args.index('request')
assert index in [0, 1], "The 'request' argument must be, at most, the second positional argument."
assert len(argspec.args) - len(argspec.defaults or []) == index, "All arguments after (and including) 'request' must have default values."
def wrapper(*args, **kwargs):
request = None
frame = inspect.currentframe().f_back
while frame and not request:
for v_name, v_object in six.iteritems(frame.f_locals):
if isinstance(v_object, HttpRequest):
request = v_object
break
frame = frame.f_back
if request:
kwargs.setdefault('request', request)
return func(*args, **kwargs)
return wrapper
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Please login first before commenting.