Django模板和locals技巧

55

《Django Book》介绍了一种本地技巧,以避免在上下文字典中输入一长串参数。

http://www.djangobook.com/en/2.0/chapter04/

示例:

def current_datetime(request):
    dt_now = datetime.datetime.now()
    return render_to_response('current.html', {'dt_now': dt_now})

变成:

def current_datetime(request):
    dt_now = datetime.datetime.now()
    return render_to_response('current.html', locals())

它建议那些懒惰的程序员使用这种技巧,但指出一些可能对性能产生影响的开销。

我想知道你们中是否有人在实际应用中使用locals技巧。你推荐使用它吗?还是说这是一种不好的做法?

8个回答

84
我不喜欢重复——我认为“DRY”(不要重复自己)是关键的编程原则。因此,在类似的情况下,我确实使用了locals()。Django模板渲染远非这种情况的唯一情况:一般情况是“接受字典的函数或运算符,但不介意字典包含额外条目”。(例如,Python中的普通字符串格式化就是另一个这样的情况)。
然而,有一个相反的原则:程序应该以尽可能本地化的方式可理解——这有助于维护和重构(因为它消除了研究其他文件以检查哪些重构是可接受的需要)。这表明,在locals()的情况下,如果模板(或字符串格式等)是本地文字(罕见的情况,只使用了少量变量,因此locals()并没有太大的优势!-),那么这是可以接受的,但在模板位于不同文件的正常情况下会出现问题。
因此,在大多数情况下,使用locals()会严重阻碍重构。在Python的几乎所有情况下,局部变量及其名称可以自由更改作为局部重构的一部分,因为它们没有“外部可见”的影响......但是使用locals()会打破这一点——突然之间,您不能安全地将变量重命名为提供更好的清晰度的不同名称,以一种消除变量需要的方式重构代码流程等等,而不必每次都研究单独的模板文件以检查旧名称是否可能不需要(并且可能编辑模板文件,这可能是非常棘手的,例如如果它用于多种不同的自然语言进行i18n / L10n)。
因此,在除了性能的次要问题之外,有强烈的压力不要在“严肃的”,“生产”代码中使用locals()——需要长期维护,因此易于重构和本地化。因此,当我“尽我所能进行编程”而不是“走捷径”时,我知道我最好避免使用locals()
你想在模板呈现的上下文中拥有的值不一定作为本地裸名自然可用;也许其中一些或许大多数是计算结果、列表或字典中的项等。在这种情况下,如果只是将这些值累积到适当的字典中而不是分配本地裸名,那么避免使用locals()的诱惑就更容易了。
这并不是一个容易权衡的问题,因为两个好原则(避免重复和具有良好的局部性)不可避免地会发生冲突 - 因此,很好的问题!这不是一个完全可以用明确的黑白答案解决的问题,这就是为什么我试图从两个方面进行扩展的原因。最终,我认为这是一种“风格”方面的问题,编程团队可能会被建议采用团队统一的风格指南并坚持下去 - 至少它消除了每次出现这个问题时需要反复做出决定的需要,并产生更加同质化(从而易于维护)的代码库。[[我必须承认,在我所在的团队的样式指南中,尽管有许多其他指南,但从未明确提到过这个特定点!-)]]

3
有几个原因支持+1。其中一个立即浮现在我脑海中的是:如果您拥有一组默认的context_processors来为所有视图提供一些标准值,那么这将发生。如果您意外地使用了一个已经明确传递到模板中的值覆盖了其中一个默认值,那已经很糟糕了,但如果是一个随意的重写,因为有一个与上下文处理器中的“全局名称”同名的local()值,那么您将会抓狂。 - Peter Rowell

28

我经常考虑以下做法,但我不确定它是否真的有帮助。

class MyStruct(object):
     pass

def my_view(request, id):
    c = MyStruct()
    c.customer = ..
    c.invoice = ..
    c.date = ..
    return render_to_response('xxx,html',c.__dict__)

8
哦,我的天啊!- 真是太棒了! - Tal Weiss
1
只需将此注释添加到类定义中:#属性将在__init__之外定义 pylint:disable = W0201 - Tal Weiss
3
为什么不直接使用简单的字典? - Gill Bates
2
因为使用简单的字典无法使用点符号表示。c.date 看起来比 c['date'] 更好。 - fmalina
3
不需要定义一个虚拟类。c = lambda :0已足以实现该功能(函数本身也是对象)。 - Quant Metropolis

14

个人而言,我不太喜欢它。除了老旧的Python格言“明确胜于含蓄”之外,可能没有其他原因可以解释我的偏好。我喜欢确切地知道我的模板中加入了什么。


我也不喜欢,它太枯燥了,如果出现错误,调试起来就不容易。 - doniyor

3

到目前为止,我使用它没有遇到任何问题。

我不是特别喜欢打字,这就是为什么我喜欢它的原因。像下面这样的代码

'customer' : customer,
'invoice' : invoice,
'date' : date

我觉得这看起来很荒谬,如果可以避免的话,我会尽量避免。我喜欢Python的一个原因是它缺乏样板代码(虽然这不是真正的样板代码,但它很相似)。


2

我猜这取决于您在函数中定义了多少本地变量。

如果恰好匹配要返回给模板的数量,或者“额外”变量是简单的结构,例如整数或布尔值,则我想没有必要显式地返回它们,因为这需要更多的工作。

但另一方面,如果您的视图具有许多复杂的“辅助”变量,例如您在视图中使用的模型实例来生成要发送到模板的数据,则您可能需要考虑使用显式变量返回到模板。


1

我知道这是一个旧的线程...目前render_to_response已经被弃用。使用不带locals()的render代替。传递所有本地变量是一种不好的做法。 这里是一个views.py的例子:

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def mybooks(request):
    entries = Book.objects.all()
    return render(request, 'mybooks.html', {'entries': entries})

render_to_response目前还没有被弃用。它将在2.0版本中被弃用,但现在还没有。 - Dracontis

1
为了减少 views.py 中的混乱,同时保持显式:在 controllers.py 中:
import sys

def auto_context(the_locals=None):
    # Take any variable in the locals() whose name ends with an underscore, and
    # put it in a dictionary with the underscore removed.

    if the_locals is None:
        # We can access the locals of the caller function even if they
        # aren't passed in.
        caller = sys._getframe(1)
        the_locals = caller.f_locals

    return dict([
        (key[:-1], value)
        for (key, value) in the_locals.items()
        if key[-1] == "_"])

在 views.py 文件中:
from app.controllers import auto_context

def a_view(request): 
    hello_ = "World" # This will go into the context.
    goodnight = "Moon" # This won't.
    return render(request, "template.html", auto_context())

template.html中,使用{{ hello }}
您不太可能意外地给变量命名下划线结尾。因此,您将确切知道要输入模板的内容。使用auto_context()或等效的auto_context(locals())。享受吧!

-1

我同意Alex的观点。当你可以这样做时,就没有必要创建一个类实例(如Niels所建议的):

def my_view(request, id):
    customer = ..
    invoice = ..
    date = ..
    return render_to_response('xxx,html', locals())

如果你想追求性能,那么点符号查找会比较慢。
如果你想追求可维护性,那么使用这种方式可以减少代码行数,同时使代码更加易读,减少不必要的结构体。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接