"Lazy load" of data from the context processor

In each view of my application, I need to prepare a navigation menu. So now, in all views, I execute a complex query and save the menu in a dictionary, which is passed to the template. In the templates, the variable in which I have data is surrounded by a "cache", so although the requests are quite expensive, this does not bother me.

But I do not want to repeat myself in all eyes. I guessed that the best place to prepare a menu is in my own context processor. And so I wrote one, but I noticed that even when I do not use data from the context processor, the queries used to prepare the menu are executed. Is there a way to "lazily load" such data from CP, or do I need to use a "low level" cache in CP? Or maybe there is a better solution to my problem?

+8
django caching django-views lazy-loading
source share
2 answers

Django has a SimpleLazyObject . In Django 1.3, this is used by the ach context handler ( source code ). This makes user accessible in the context of the template for each request, but the user only gets access if the template contains {{ user }} .

You should be able to do something similar in your processor context.

 from django.utils.functional import SimpleLazyObject def my_context_processor(request): def complicated_query(): do_stuff() return result return { 'result': SimpleLazyObject(complicated_query) 
+18
source share

If you pass the called object to the template context, Django will evaluate it when it will be used in the template. This provides an easy way to do laziness - just go to callables:

 def my_context_processor(request): def complicated_query(): do_stuff() return result return {'result': complicated_query} 

The problem with this is that it does not memoize the call - if you use it several times, complicated_query is called several times.

The fix is ​​to use something like SimpleLazyObject , as in another answer, or use something like this memoize decorator:

 def memoize_nullary(f): """ Memoizes a function that takes no arguments. """ def func(): if not hasattr(func, 'retval'): func.retval = f() return func.retval return func def my_context_processor(request): @memoize_nullary def complicated_query(): do_stuff() return result return {'result': complicated_query} 

Or, if the function already exists, you would do it like this:

 from somewhere import complicated_query def my_context_processor(request): return {'result': memoize_nullary(complicated_query)} 

I would prefer this method over SimpleLazyObject , because the latter can sometimes generate some strange errors .

(I was the one who originally implemented LazyObject and SimpleLazyObject , and discovered that there is a curse on any code artifact marked simple .)

+3
source share

All Articles