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

时间:2011-12-19 15:51:13

标签: django caching django-views lazy-loading

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

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

2 个答案:

答案 0 :(得分:18)

Django有SimpleLazyObject。在Django 1.3中,auth context processorsource code)使用了它。这使得每个查询的模板上下文中都可以使用user,但只有在模板包含{{ user }}时才会访问该用户。

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

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

    return {
        'result': SimpleLazyObject(complicated_query)

答案 1 :(得分:3)

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

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

问题在于它没有记住调用 - 如果你多次使用它,complicated_query会被多次调用。

修复是使用SimpleLazyObject之类的东西,就像在另一个答案中一样,或者使用像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}

或者,如果该功能已经存在,您可以这样做:

from somewhere import complicated_query

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

我希望这种方法优于SimpleLazyObject,因为后者可以产生一些strange bugs sometimes

(我是最初实施LazyObjectSimpleLazyObject的人,并为自己发现任何标有simple的代码人工制品都存在诅咒。)