Django newforms
由于前两天被 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>
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>
这个例子表明在子类化 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