来自上下文处理器的数据的“延迟加载”

时间:2021-10-19 03:50:11

In each view of my application I need to have navigation menu prepared. So right now in every view I execute complicated query and store the menu in a dictionary which is passed to a template. In templates the variable in which I have the data is surrounded with "cache", so even though the queries are quite costly, it doesn't bother me.

在我的应用程序的每个视图中,我需要准备导航菜单。所以现在在每个视图中我执行复杂的查询并将菜单存储在传递给模板的字典中。在模板中,我拥有数据的变量被“缓存”包围,所以即使查询成本很高,也不会打扰我。

But I don't want to repeat myself in every view. I guessed that the best place to prepare the menu is in my own context processor. And so I did write one, but I noticed that even when I don't use the data from the context processor, the queries used to prepare the menu are executed. Is there a way to "lazy load" such data from CP or do I have to use "low level" cache in CP? Or maybe there's a better solution to my problem?

但我不想在每一种观点中重复自己。我猜想准备菜单的最佳位置是我自己的上下文处理器。所以我写了一个,但我注意到,即使我不使用来自上下文处理器的数据,也会执行用于准备菜单的查询。有没有办法从CP“延迟加载”这样的数据,还是必须在CP中使用“低级”缓存?或者也许有更好的解决方案来解决我的问题?

2 个解决方案

#1


18  

Django has a SimpleLazyObject. In Django 1.3, this is used by the auth context processor (source code). This makes user available in the template context for every query, but the user is only accessed when the template contains {{ user }}.

Django有一个SimpleLazyObject。在Django 1.3中,auth上下文处理器使用它(源代码)。这使得用户可以在每个查询的模板上下文中使用,但仅在模板包含{{user}}时才访问该用户。

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

您应该能够在上下文处理器中执行类似的操作。

from django.utils.functional import SimpleLazyObject
def my_context_processor(request):
    def complicated_query():
        do_stuff()
        return result

    return {
        'result': SimpleLazyObject(complicated_query)

#2


3  

If you pass a callable object into the template context, Django will evaluate it when it is used in the template. This provides one simple way to do laziness - just pass in callables:

如果将可调用对象传递给模板上下文,Django将在模板中使用它时对其进行评估。这提供了一种简单的方法来做懒惰 - 只需传入callables:

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

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

这样做的问题是它不会记忆调用 - 如果多次使用它,则会多次调用complex_query。

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

修复是使用类似SimpleLazyObject的东西,如在另一个答案中,或使用像这样的memoize装饰器:

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 produce some strange bugs sometimes.

我更喜欢这种方法而不是SimpleLazyObject,因为后者有时会产生一些奇怪的错误。

(I was the one who originally implemented LazyObject and SimpleLazyObject, and discovered for myself that there is curse on any code artefact labelled simple.)

(我是最初实现LazyObject和SimpleLazyObject的人,并为自己发现任何标记为简单的代码工件都存在诅咒。)

#1


18  

Django has a SimpleLazyObject. In Django 1.3, this is used by the auth context processor (source code). This makes user available in the template context for every query, but the user is only accessed when the template contains {{ user }}.

Django有一个SimpleLazyObject。在Django 1.3中,auth上下文处理器使用它(源代码)。这使得用户可以在每个查询的模板上下文中使用,但仅在模板包含{{user}}时才访问该用户。

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

您应该能够在上下文处理器中执行类似的操作。

from django.utils.functional import SimpleLazyObject
def my_context_processor(request):
    def complicated_query():
        do_stuff()
        return result

    return {
        'result': SimpleLazyObject(complicated_query)

#2


3  

If you pass a callable object into the template context, Django will evaluate it when it is used in the template. This provides one simple way to do laziness - just pass in callables:

如果将可调用对象传递给模板上下文,Django将在模板中使用它时对其进行评估。这提供了一种简单的方法来做懒惰 - 只需传入callables:

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

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

这样做的问题是它不会记忆调用 - 如果多次使用它,则会多次调用complex_query。

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

修复是使用类似SimpleLazyObject的东西,如在另一个答案中,或使用像这样的memoize装饰器:

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 produce some strange bugs sometimes.

我更喜欢这种方法而不是SimpleLazyObject,因为后者有时会产生一些奇怪的错误。

(I was the one who originally implemented LazyObject and SimpleLazyObject, and discovered for myself that there is curse on any code artefact labelled simple.)

(我是最初实现LazyObject和SimpleLazyObject的人,并为自己发现任何标记为简单的代码工件都存在诅咒。)