使用django,我们可以以声明式的方式来定义一个form,如下:
# -*- coding: utf-8 -*- from django import forms class simpleform(forms.form): field_a = forms.charfield(max_length=100) field_b = forms.charfield(max_length=100)
写起来很舒服,但是问题来了,当我把这个form初始化之后,比如:
from polls.forms import simpleform
sf = simpleform({'field_a':'value of field_a', 'field_b':'value of field_b'})
然后在python shell里执行dir(sf),发现该实例并没有field_a和field_b这两个属性,显然我们就不能像sf.field_a这么来引用sf上的字段了。可是明明我们可以在template里以{{ form_name.field_name }}的形式来引用form的字段,这是怎么回事呢?
一番调查之后发现背后的实现机制还比较曲折。首先,如果我们要引用form里的字段应该怎么写呢?应该这么写:sf['field_a']
为什么要这么写呢?上代码,django.forms.baseform的__getitem__方法:
def __getitem__(self, name): returns a boundfield with the given name. try: field = self.fields[name] except keyerror: raise keyerror('key %r not found in form' % name) return boundfield(self, field, name)
这样就把baseform变成一个像dict一样的的容器了,所以可以用上面的语法来引用form里的字段。
新的问题又来了,为什么可以在template里以{{ form_name.field_name }}的形式来引用form的字段呢?见django的官方文档:https://docs.djangoproject.com/en/1.6/topics/templates/#variables。原来django的模板引擎碰到{{ form_name.field_name }}这样的表达式,会在form_name对象上运行字典查找,所以模板引擎对{{ sf.field_a }}求值时实际上运行了sf['field_a'],真相大白了。
另外,上文中的simpleform的类型实际上是django.forms.declarativefieldsmetaclass。这个元类实际上是把simpleform中以声明式语法声明的所有字段(还包括父类中的声明式字段)通过get_declared_fields方法转换成了一个dict,并将dict的值赋给了将要生成的类的base_fields属性,然后基于simpleform生成了一个新的类。