由于前两天被 django forms 中的 manipulator 弄怕了,觉得这个东西比较难用。在 python-chinese 邮件列表中提问后,按照建议,我开始学习 newforms 了。本文是一些摘要。
newforms 的第一个好处是不必和 model 耦合,专门负责 html 的显示和验证。并且调试相对简便,我们随时可以通过 print 来输出其将要 render 的 html.
如何定义 Form 类
Form 类可以分为两种:绑定数据的和不绑定数据的。对于绑定数据的 Form, 可以进行验证(validate).
而 Form 可以有多种办法来创建。可以通过子类化的方式完全定制(继承 django.newforms.Form);也可以用一些帮助方法,直接返回一个定制的 Form 实例。比如:
if
id
is
None:
EntryForm = forms.models.form_for_model(Entry)
else :
entry = Entry.objects.get(id = id)
EntryForm = forms.models.form_for_instance(entry)
EntryForm = forms.models.form_for_model(Entry)
else :
entry = Entry.objects.get(id = id)
EntryForm = forms.models.form_for_instance(entry)
这段代码演示了从模型或模型类的实例直接创建相应 Form 的用法。对于简单的情形这样做应该是没问题的。
完全定制的做法:
>>>
class
PersonForm(Form):
first_name
=
CharField()
last_name
=
CharField()
>>> class InstrumentForm(Form):
instrument
=
CharField()
>>> class BeatleForm(PersonForm, InstrumentForm):
haircut_type
=
CharField()
>>> b = BeatleForm(auto_id = False)
>>> print b.as_ul()
< li > First name: < input type = " text " name = " first_name " /></ li >
< li > Last name: < input type = " text " name = " last_name " /></ li >
< li > Instrument: < input type = " text " name = " instrument " /></ li >
< li > Haircut type: < input type = " text " name = " haircut_type " /></ li >
![](/Images/dot.gif)
![](/Images/dot.gif)
>>> class InstrumentForm(Form):
![](/Images/dot.gif)
>>> class BeatleForm(PersonForm, InstrumentForm):
![](/Images/dot.gif)
>>> b = BeatleForm(auto_id = False)
>>> print b.as_ul()
< li > First name: < input type = " text " name = " first_name " /></ li >
< li > Last name: < input type = " text " name = " last_name " /></ li >
< li > Instrument: < input type = " text " name = " instrument " /></ li >
< li > Haircut type: < input type = " text " name = " haircut_type " /></ li >
这个例子表明在子类化 Form 的时候,甚至可以用多继承。
Form 的实例化
Form 的构造器中可以传递一个字典,代表其中包含的数据,这种情况下 Form 被称之为是“绑定的”;而如果不传递任何数据,则称为“非绑定的”。
#
绑定数据的 Form
f = SomeForm({ " email " : " test@test.com " , " username " : " abc " })
# 非绑定的 Form
f2 = SomeForm()
f = SomeForm({ " email " : " test@test.com " , " username " : " abc " })
# 非绑定的 Form
f2 = SomeForm()
通过 f.is_bound 可以判断一个 form 是否绑定了数据。需要注意的是,数据目前被设计为不可变的,即一旦传递给 f 对象的构造器,则没有办法改变它。
Form 的输出形式
Form 的输出形式,默认的有如下几种:
print
f.as_p()
#
每个字段输出为一个 <p/> 标签
print f.as_table() # 表格,这是默认情形
print f.as_ul() # <ul/>
print f # 和 print f.as_table() 等效
print f.as_table() # 表格,这是默认情形
print f.as_ul() # <ul/>
print f # 和 print f.as_table() 等效
验证数据
直接用 f.is_valid() 即可。这个操作对非绑定的 Form 无意义。
如果存在错误,则 f.errors 中包含了各字段的错误信息。该属性是一个字典,以 field 名称为 key.
如果你直接访问 f.errors,并不需要先调用 f.is_valid(),Django 会自动调用它的。
newforms 设计中的另一个好处是,通过 f.clean_data 可以获取验证后的数据。这些数据的格式是统一的形式,不管他们输入时是什么样的。比如对于日期字段,通过这个属性,最终得到的都是 python 的 datetime.date 类型。
另外一个重要的话题是关于 Fields,这一块比较琐碎,具体的描述可以看文档。值得记一下的几个要点:
1. Field 的 clean() 方法,用于验证数据;
2. Field 默认是必填的;(对应于构造器中的 required 参数)
3. Field 可以指定用于输出 HTML 的具体 widget(小控件)。
比如我们要把一个字段显示为富文本控件,就可以创建自定义的 Widget 来实现。
代码示例
摘自: http://code.pui.ch/2007/01/07/using-djangos-newforms/
def
add_edit_entry(request, id
=
None):
if id is None:
EntryForm = forms.models.form_for_model(Entry)
else :
entry = Entry.objects.get(id = id)
EntryForm = forms.models.form_for_instance(entry)
EntryForm.fields[ ' detail ' ].widget = TinyMCE()
if request.method == ' POST ' :
form = EntryForm(request.POST)
if form.is_valid():
entry = form.save(commit = False)
if id is None:
entry.owner = request.user
entry.save()
return HttpResponseRedirect( " / " )
else :
form = EntryForm()
t = loader.get_template( ' add_entry.html ' )
c = Context({
' form ' : form,
' html_head ' : ' <script src="/media/tiny_mce/tiny_mce_src.js" type="text/javascript"></script> '
})
return HttpResponse(t.render(c))
if id is None:
EntryForm = forms.models.form_for_model(Entry)
else :
entry = Entry.objects.get(id = id)
EntryForm = forms.models.form_for_instance(entry)
EntryForm.fields[ ' detail ' ].widget = TinyMCE()
if request.method == ' POST ' :
form = EntryForm(request.POST)
if form.is_valid():
entry = form.save(commit = False)
if id is None:
entry.owner = request.user
entry.save()
return HttpResponseRedirect( " / " )
else :
form = EntryForm()
t = loader.get_template( ' add_entry.html ' )
c = Context({
' form ' : form,
' html_head ' : ' <script src="/media/tiny_mce/tiny_mce_src.js" type="text/javascript"></script> '
})
return HttpResponse(t.render(c))
add_entry.html
{% extends "base.html" %}
{% block content %}
< form action ="." method ="post" >
< table class ="form" >
{{ form }}
</ table >
< input type ="submit" value ="speichern" />
</ form >
{% endblock %}
{% block content %}
< form action ="." method ="post" >
< table class ="form" >
{{ form }}
</ table >
< input type ="submit" value ="speichern" />
</ form >
{% endblock %}
以上这段代码是个简单的范本,其中没有用到自定义 Form. 因此更灵活的用法还需要我们继续去发掘。
参考资源
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py
http://code.pui.ch/2007/01/07/using-djangos-newforms/
http://www.mikecantelon.com/?q=node/22