- Author:
- spookylukey
- Posted:
- December 12, 2013
- Language:
- Python
- Version:
- Not specified
- Score:
- 0 (after 0 ratings)
Sometimes you have context variables that are needed on many pages in a site, but not all. You only want them to be evaluated when actually needed, especially if they are expensive calculations that do DB queries etc. The pattern to use is shown: put a callable into the context, not the final value, and also wrap the callable with memoize_nullary.
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | # Sometimes you have context variables that are needed on many pages in a site,
# but not all. You only want them to be evaluated when actually needed, especially
# if they are expensive calculations that do DB queries etc. The pattern to use is
# shown: put a callable into the context, not the final value, and also wrap the
# callable with memoize_nullary.
#
# This solution uses the fact that the Django template language will detect and
# call any callables when they are referred to. So, if a template contains:
#
# {{ foo }}
#
# then it will look up 'foo'. If 'foo' is callable, the result will be the output
# of foo().
#
# So, we can pass in a function or lambda to the template context instead of a
# final value.
#
# For expensive calculations, however, we need to ensure that the callable will
# only be called once. The template language doesn't ensure this. So if you have:
#
# {% if permissions %}
# {% for p in permissions %}
# ...
# {% endfor %}
# {% endif %}
#
# and 'permissions' is callable, then 'permissions' will be called twice.
#
# This can be fixed using the 'memoize_nullary' utility. Note that this is
# applied inside the my_context_processor function, so that every time
# 'my_context_processor' is called, memoize_nullary gets called, and returns
# fresh function objects. If you applied memoize_nullary at the module level,
# subsequent calls to my_context_processor from subsequent requests would end up
# outputting values from the previous request.
# Utility needed:
def memoize_nullary(f):
"""
Memoizes a function that takes no arguments. The memoization lasts only as
long as we hold a reference to the returned function.
"""
def func():
if not hasattr(func, 'retval'):
func.retval = f()
return func.retval
return func
# Example:
def expensive_calculation():
return sum(range(1, 1000000))
def expensive_calc_2(request):
return request.user.permission_set.all()
def my_context_processor(request):
return {
'numbers': memoize_nullary(expensive_calculation),
'permissions': memoize_nullary(lambda: expensive_calc_2(request)),
# Could also do it inline like this:
# 'permissions': memoize_nullary(request.user.permission_set.all)
# or
# 'permissions': memoize_nullary(lambda: request.user.permission_set.all())
}
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 9 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 4 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 5 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Please login first before commenting.