Created
April 3, 2014 17:17
-
-
Save martinrusev/9958739 to your computer and use it in GitHub Desktop.
Django lazy context processor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://djangosnippets.org/snippets/3011/ | |
# 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()) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment