最近,我在我们的Django网络应用程序上做了一些工作,我想对一个表单的一个特定字段做一些特别的处理。按照Django表单的惯例,我们用'{% for %}'模板标签来迭代表单字段。 剥开后,这就像:
{% for field in form %}
{{ field.label_tag }} <br>
{{ field }}
{% endfor %}
我必须让一个字段产生额外的HTML,这意味着我必须以某种方式识别它。起初我在标签上使用了'{% if %}',但这感觉不对;标签是显示给用户的文本,我们可能想改变它。所以我在互联网上做了一些搜索,发现了'field.name'(也许来自这里),这是每个字段的实际名称。我把它放了进去,一切正常,我继续前进,几天后,我被一个Python问题击中了,即表单字段如何知道它的名字?
表单字段被定义为看起来像类属性的东西:
class RequestForm(forms.Form):
login = forms.CharField([...])
[...]
然而,Python对象不知道自己的名字(有充分的理由),包括类或类实例上的属性。起初我以为Django使用了元类的魔法,所以在定义RequestForm类时就设置了RequestForm.login.name ,但是表单类原来比这更神奇,一旦你提取了相关对象,它们就没有.name 属性。
这时我意识到(并记住了),你在Django模板中写的伪Python代码与你在应用中写的Python代码是不一样的。你不能假设对其中一个有用的东西(这里指的是'field.name')会对另一个有用,而且是双向的。有可能撕开魔法的盖子,找到Django的解释,但它会把你引向几个层次的深处,你的Django模板代码与你在Python视图代码中写的还是不一样的。
PS:在我的例子中已经显示了一点神奇的地方,那就是field.label_tag 是一个方法,而不是一个属性。Django模板语言会自动调用它并使用其结果。
旁白:这里发生了什么
这里使用的 'field' 模板变量最终保存了一个真正的 Python 对象,但它并不是来自你认为的那个类。正如在循环处理表单字段中所涉及的,'field' 对象是一个 BoundField 实例。实际的 Field 实例,也就是你的视图代码所处理的东西,隐藏在field.field 中。BoundField它有一个.name 属性,这个属性是在被整个表单代码初始化时设置的。整个表单代码确实知道每个字段的名称(而且必须知道)。
(有点出乎我的意料,.field 是一个普通的实例属性,而不是什么属性或花哨的东西。你可以看到它在boundfield.py 中被设置了)。