表单
这一章节中,我们将学习如何通过web表单获取数据。根据django关于表单的官方文档,表单处理通过如下功能:
➊自动生成表单控件展现HTML表单(比如文本框或日期选择器);
➋检查提交的数据是否违反了一系列验证规则;
➌一旦出现错误重新展现一个表单;
➍将提交的表单数据转换成相关的python数据类型。
使用django表单功能的优点之一就是创建HTML表单时能为你节省很多时间和麻烦。
7.1 基本流程
创建表单并处理用户输入的基本步骤如下所示:
➊如果你还没有在django应用目录里创建forms.py文件的话,先创建一个用来存储与表单相关的类。
➋为每个你希望要表示成表单的模型创建一个ModelForm类。
➌按照你的需求设计表单。
➍创建一个视图处理表单
- 包括显示表单
- 保存表单数据
- 当用户在表单中提交不正确的数据(或未提交数据)时,标记会发生的错误。
➎创建一个模板显示表单。
➏增加一个URL模式映射新视图。
7.2 网页和分类表单
定义ModelForm 的子类
在forms.py里,我们将创建几个类并继承django的ModelForm类。实际上,ModelForm类就是一个帮助类,它允许你从一个已存在的模型创建一个django的表单。目前我们已经在rango中创建了两个模型(Category和Page),我们现在就创建两个ModelForms。
在rango/forms.py文件里,添加如下代码:
from django import forms
from rango.models import Page,Category
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128,help_text="Please enter the category name.")
views= forms.IntegerField(widget=forms.HiddenInput(),initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
slug = forms.CharField(widget=forms.HiddenInput(),required=False)
class Meta:
model = Category
fields = ('name',)
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128,help_text="Please enter the title of the page.")
url = forms.URLField(max_length=200,help_text="Please enter the URL of the page.")
views = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
class Meta:
model = Page
exclude = ('category',)
# def clean(self):
# clean_data = self.cleaned_data
# url = cleaned_data.get('url')
# if url and not url.startswith('http://'):
# url = 'http://' + url
# clearn_data['url'] = url
# return clearn_data
编写添加分类视图
在CategoryForm类的定义中,我们已经创建了一个新视图展现表单并处理提交上来的表单数据。添加如下代码到rango/views.py。
from rango.forms import CategoryForm
def add_category(request):
form = CategoryForm()
if request.method == 'POST':
form = CategoryForm(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print(form.errors)
return render(request,'rango/add_category.html',{
'form':form})
- 展示一个新的空的增加分类的表单;
- 将用户提交的表单数据保存到相应的模型中并渲染到rango的主页上;
- 如果出现错误,会重新展示带错误的表单。
注意调用render()的那一行,我们引用了一个新的叫add_category.html的模板。这个模板会包含一些与表单相关的模板代码和HTML。
创建添加分类页面的模板
创建文件templates/rango/add_category.html,并填写如下代码。
<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>
<body>
<h1>Add a Category</h1>
<div>
<form id="category_form" method="post" action="/rango/add_category/">
{
% csrf_token %}
{
% for hidden in form.hidden_fields %}
{
{
hidden }}
{
% endfor %}
{
% for field in form.visible_fields %}
{
{
field.errors }}
{
{
field.help_text }}
{
{
field }}
{
% endfor %}
<input type="submit" name="submit" value="Create Category"/>
</form>
</div>>
</body>
</html>
你应该注意到这个代码段{% csrf_token %}。这是一个跨站请求伪造令牌,用来保护在随后提交表单时启动的HTTP的POST请求。django框架需要显式列出CSRF令牌。如果在表单中忘了加上一个CSRF令牌,提交表单时就有可能报错。
映射添加分类视图
现在我们需要将add_category()视图函数映射到URL上。在模板中,我们在表单的action属性里使用了/rango/add_category,现在我们需要创建一个从URL到视图的映射了。在rango/urls.py中,按照如下代码修改URL模式。
from django.conf.urls import url
from rango import views
urlpatterns = [
url(r'^$',views.index,name='index'),
url(r'^add_category/$',views.add_category,name='add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$',views.show_category, name='show_category'),
#url(r'about/$', views.about, name='about'),
]
修改首页视图
最后一步让我们在index主页上添加一个链接,通过这个链接可以让我们直接的添加分类。编辑模板rango/index.html,如下所示,在about链接的div元素里添加一个超链接。
<div>
<a href="/rango/add_category/">Add a New Category</a><br />
</div>
效果检验
清理表单数据
在某些场景中,用户有可能输入了一些不完全正确的数据。我们可以重写ModelForm类中定义的clean()方法。这个方法在将表单数据保存到新的模型实例之前被调用,因此给我们提供了一个逻辑空间插入代码用来验证甚至是修复用户输入的表单数据,我们可以检查用户输入的url值是否以http://开头,如果不是,我们可以为用户的输入预添加http://。
def clean(self):
clean_data = self.cleaned_data
url = cleaned_data.get('url')
if url and not url.startswith('http://'):
url = 'http://' + url
clearn_data['url'] = url
return clearn_data
7.3 练习
在添加分类表单中如果不输入分类名称会发生什么?
如果添加的分类已经存在会发生什么?
如果访问不存在的分类会发生什么?后文有提示。
定义 ModelForm 子类时,我们为某些字段设定了 max_length 参数,这与模型中的验证重复了。想想如何重构,以免重复指定 max_length 值?
让用户能在分类中添加网页。参见下面的示例代码和提示。
基本步骤与添加分类一样。
❏ 编写一个视图,add_page()
from rango.forms import PageForm
def add_page(request,category_name_slug):
try:
category = Category.objects.get(slug = category_name_slug)
except Category.DoesNotExist:
category = None
form = PageForm()
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if category:
page = form.save(commit=False)
page.category = category
page.views = 0
page.save()
return show_category(request,category_name_slug)
else:
print(form.errors)
context_dict = {
'form':form,'category':category}
return render(request,'rango/add_page.html',context_dict)
❏ 创建一个模板,rango/add_page.html
<!DOCTYPE html>
<html>
<head>
<title>Rango - Add Page</title>
</head>
<body>
{
% if category %}
<h1>Add a Page to {
{
category.name}}</h1>
<form id="page_form" method="post" action="/rango/category/{
{ category.slug }}/add_page/">
{
% csrf_token %}
{
% for hidden in form.hidden_fields %}
{
{
hidden }}
{
% endfor %}
{
% for field in form.visible_fields %}
{
{
field.errors }}
{
{
field.help_text }}
{
{
field }}
{
% endfor %}
<input type="submit" name="submit" value="Add Page" />
</form>
{
% else %}
The specified category does not exist!
{
% endif%}
</body>
</html>
❏ 添加一个 URL 映射
from django.conf.urls import url
from rango import views
urlpatterns = [
url(r'^$',views.index,name='index'),
url(r'^add_category/$',views.add_category,name='add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$',views.show_category, name='show_category'),
#url(r'about/$', views.about, name='about'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/add_page/$', views.add_page, name='add_page'),
]
❏ 更新分类页面的模板和视图,加上添加网页链接
<a href="/rango/category/{
{category.slug}}/add_page/">Add Page</a> <br/>