目前的环境
|
|
一个简单的表单示例
|
|
通过使用manager.py shell
命令本地测试这个form类(IPython环境)。MyTestForm
继承forms.Form
,声明式的定义每个field的具体类型和属性等。
|
|
按照python的语法,my_test_field
只是MyTestForm
的类属性而不是实例属性,那么不同的实例如何区分。实际上MyTestForm
只是一个声明,django通过元编程(超类)会生成真正的用于实例化的类,不妨称为真实类。具体情况结合源码分析。
|
|
可以看到,由于超类的使用用户可以通过声明式的方式定义自己的表单类。但也会使得类不在以默认的方式实现,从而源码可读性变差。
with_metaclass的演变
关于six.with_metaclass
有一些演变导致现在的可读性更差,现在试图分析下原因,源码如下。
|
|
旧版本中with_metaclass
直接返回一个由超类动态生成的中介类作为forms.Form的基类,继承关系比较简单MyTestForm->Form-> 中介类 -> meta->真实类
。而现在的形式更加复杂,在于生成中介类和真实类使用了不同的方法,中介类使用type.__new__(cls, name, (), d)
,真实类使用meta(name, bases, d)
,这样做的主要好处是使最后真实类的类型和继承关系更加清楚。
|
|
form表单
回过头看一下MyClassForm.as_p()
中的各部分如何运作。
|
|
field类
每类field是一种表单域,每个field处理lable、单选框、复选框、错误提示、验证等。如示例中所示,field通过声明为form类的属性而定义。form会将所有的field实例收集到base_fields属性中并在实例化时赋值给实例的fields,再通过改写__getitem__
方法实现field = form[fieldname]
获取BoundField
实例。在form.as_p
中涉及到了一些field的操作,现在跟踪下。
|
|
简单的继承关系图
|
|
BoundField
BoundField是一个存有数据的Field,主要是提供接口(类似代理模式)以及html格式输出。默认情况,BoundField输出对应类型Field的html。
|
|
django选择通过实现一个BoundFiled代理类统一处理field的输出,而不是在基类中实现接口将实现分散在各Field类型中。
widget
widget是表单中的小部件处理表单域的输入以及验证。Widget类似Form是由超类实现,超类的作用是为Widget实现media属性,具体的作用稍后分析。不同widget对应input标签中的type属性。
|
|
widget的继承关系
|
|