您的位置:首页 > 编程语言 > Go语言

第八章 Fun with Forms

2015-09-11 16:33 429 查看
到目前为止,我们只提出了通过我们已经创建的意见和模板数据。在本章中,我们将贯穿如何通过网页表单形式获取数据。Django自带了一些巧妙的表单处理的功能,使其成为一个非常简单的过程即从用户收集信息并将其发送回你的Web应用程序。根据Django的表单文档,表格处理功能允许你:

显示与自动生成的表单控件(如文本框或日期选择器)的HTML表单;

检查提交的数据是否违反一系列验证规则;

重新显示表单的验证错误;

转换提交的表单数据为相应的Python数据类型

对使用Django的表单功能的主要优势在于,它可以为您节省大量的时间和HTML的麻烦。本教程的这一部分将着眼于如何实现必要的基础结构,使Rango的用户通过表单将分类和页添加到数据库中。

基本工作流

这基本步骤包括创建一个表单并允许用户通过表单形式输入数据如下所示:

如果您尚未得到了一个,在你的Django应用程序的目录中创建一个
forms.py
文件来存储表单相关的类。

为每个模型创建一个你希望的
ModelForm
类来表示表单。

自定义你想要的表单。

创建或更新一个视图处理表单-包括表单的显示,存储表单数据和标记若用户输入不正确的数据(或无数据的话)的表单可能会发生的错误。

创建或更新一个模板显示表单。

添加一个
urlpattern
映射到新的视图(如果你已经创建了一个)。

这个工作流程比以前的工作流程更加复杂一些,我们必须构建更多的复杂性的视图。然而,一旦你处理几次这样的过程你就会很清楚一切片段组合在一起。

页面和分类表

首先,在
rango
应用程序目录下创建一个文件叫
forms.py
。虽然这一步不是绝对必要的,你也可以把表单放入
models.py
,这使得代码更清洁和更清晰的理解。

创建ModelForm类

在Rango的
forms.py
模块中,我们会创建许多类集成于Django的
ModelForm
。从本质上讲,一个ModelForm是一个帮助类允许你创建一个Django的
form
从一个已存在的模型。正如我们已经得到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)

# An inline class to provide additional information on the form.
class Meta:
# Provide an association between the ModelForm and a model
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:
# Provide an association between the ModelForm and a model
model = Page

# What fields do we want to include in our form?
# This way we don't need every field in the model present.
# Some fields may allow NULL values, so we may not want to include them...
# Here, we are hiding the foreign key.
# we can either exclude the category field from the form,
exclude = ('category',)
#or specify the fields to include (i.e. not include the category field)
#fields = ('title', 'url', 'views')


#TODO(leifos)
:需要注意的是在Django 1.7 +它现在需要通过
Fields
指定所包含的字段,或通过
exclude
指定要被排除的字段。

Django的为我们提供了多种方式自定义,是维护我们创建的表单。在上面的代码示例中,我们指定我们希望使用的每个字段要现实的小部件。例如,在我们的
PageForm
类中,我们为
title
字段定义
forms.CharField
和为
URL
字段定义
forms.Field
。这两个字段为用户提供文本输入。注意我们为字段提供
max_length
参数,我们指定的长度是相同的,我们在基础数据模型指定的各个字段的最大长度。

回到第6章来检查自己,或者看看Rango的
models.py
文件。你还会注意到我们已经包括几个
IntegerField
条目的视图和likes字段在每个表单。请注意,我们已经设置了窗口小部件被隐藏的参数设置
widget=forms.HiddenInput()
,然后设置该值为0
initial=0
。这是一种将字段设置为零而不给予控制用户的字段将被隐藏,但表单将提供值给模型。然而,正如你可以在PageForm看到,尽管我们有一个隐藏字段,我们仍需要包含在表单中的字段。如果在
fields
排除
views
,那么表单将不包含该字段(尽管被指定),所以这些表单不会返回该值为0的字段。这可能会引发错误,具体取决于已经建立的模型。如果在模型中,我们指定这些字段的
default=0
,我们可以依靠模型自动填充字段默认值,从而避免一个
not null
错误。在这种情况下,它不需要这些隐藏字段。我们还包括表单中的字段
slug
,并将其设置为使用
widget=forms.HiddenInput()
,而不是指定一个初始或默认值,我们已经说过字段不是表单必须的。这是因为我们的模型将负责对
save()
来填充这个字段。从本质上讲,你需要谨慎当你定义你的模型和表单确保表单包含和传递所需的所有数据填充模型正确。

除了
CharField
IntegerField
之外,还有更多可供使用。作为一个例子,Django提供
EmailField
(对于E-mail地址的目录),
ChoiceField
(单项输入按钮),和
DateField
(日期/时间输入)。还有许多其他的字段类型,您可以使用它为你执行错误检查(例如,是否提供了一个有效的整数)。我们强烈推荐你看看部件的Django官方文档,看看什么组件存在和什么参数你可以提供自定义它们。

或许一个类继承于
ModelForm
需要定义的最重要的方面是我们要提供一个表单模型。我们关心这个通过我们的嵌套
Meta
类。为你希望使用的模型设置嵌套
Meta
类的
model
属性。例如,我们的
CategoryForm
类引用到
Category
模型。这是使Django的采取创建指定模型的图像在表单的护理很关键的一步。这也将有助于在处理标记的任何错误,以及在表单中保存和显示数据。

我们也使用
Meta
类来指定哪些字段,我们希望在我们的形式,包括通过
fields
元组。使用元组字段名称来指定您希望包含的字段。

注释

我们强烈推荐你看看官方的Django文档表单了解更多的信息关于如何定制它们。

创建一个Add Category 视图

随着我们的
CategoryForm
类已经定义,我们现在准备创建一个新视图来显示表单并处理表单数据的发布。要做到这一点,添加以下代码到
rango/views.py


from rango.forms import CategoryForm

def add_category(request):
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)

# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
form.save(commit=True)

# Now call the index() view.
# The user will be shown the homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = CategoryForm()

# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request, 'rango/add_category.html', {'form': form})


新的add_category()视图添加几个关键部分的功能来处理表单。首先,我们检查HTTP请求方法,来确定它是一个HTTP的GET或POST。我们可以适当地处理不同的请求方法–我们是否想要展示一个表单(如果这是一个GET),或处理表单数据(如果这是POST)–所有相同的URL。add_category()视图函数可以处理三种不同的情景:

显示出一个新的空白表单中添加的类别;

保存由用户提供的表单数据到关联模型,并渲染到Rango主页;

如果有错误,重新显示表单和错误消息。

注释

我们所指的GET和POST是什么?他们是两种不同类型的HTTP请求。
一个HTTP GET用来请求指定资源的表现,换言之,我们使用的HTTP GET来检索特定资源,无论它是一个网页,图像或其他文件。

与此相反,一个HTTP POST从客户端的web浏览器的提交数据进行处理。这种类型的请求被提交的HTML表单的内容时使用的例子。

最终,一个HTTP POST最终可能被编程到在服务器上创建新的资源(例如,一个新的数据库条目)。这可在以后通过HTTP GET请求来访问。

Django的表单处理机制处理通过HTTP POST请求从用户的浏览器返回的数据。它不仅处理表单数据的保存到所选的模型,但也会为每个表单字段自动生成任何错误消息(如果有需要)。这意味着Django不会存储任何提交的表单缺失信息可能会导致对数据库的引用完整性的问题。例如,该种类名字段供给没有值将返回一个错误,作为字段不能为空。

你会发现从我们称之为render()我们指的是新的模板名为
add_category.html
其中将包含有关Django的模板代码和HTML表单和页面。

创建Add Category 模板

创建文件
templates/rango/add_category.html
。在文件中,添加以下HTML标签和Django模板代码。

<!DOCTYPE html>
<html>
<head>
<title>Rango</title>
</head>

<body>
<h1>Add a Category</h1>

<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>
</body>

</html>


现在,这个代码是做什么的?你可以看到,在HTML页面的
<body>
中,我们放置一个
<form>
元素。看着为
<form>
元素的属性,你可以看到,在这种表单捕获的所有数据被发送到URL
/rango/add_category/
作为HTTP POST请求(该方法属性是不区分大小写,所以你可以做
POST
post
- 两者都提供相同的功能)。在表格中,我们有两个for循环 - 一个控制隐藏表单域,其他可见的窗体域 - 由可见域通过你的
ModelForm Meta
类的
fields
属性控制。这些循环为每个表单元素产生的HTML标记。可见的表单字段,我们也加入任何可能出现的错误与特定字段和帮助文本可以用来向用户解释他或她需要输入。

注释

隐藏的必要性以及可见表单字段需要HTTP是无状态的协议的事实。它可以使Web应用程序的某些部分难以实现不同的HTTP请求之间不能持久状态。为了克服这个限制,创建隐藏的HTML表单域,允许Web应用程序传递一个HTML表单到客户端的重要信息(这不能被看见在呈现的页面),仅发送回源服务器,当用户提交表格。

你也应该注意的代码片段
{% csrf_token %}
。这是一个跨站点请求伪造(CSRF)令牌,这有助于保护并固定在随后提交一个形式下开始的HTTP POST操作。Django框架需要该CSRF令牌。如果你忘了在表单中使用CSRF令牌,一个用户当他或她提交表单时可能会遇到错误。看看官方的Django CSRF tokens文档了解更多信息。

映射Add Category 视图

现在我们需要映射
add_category()
视图到一个URL。在模板中,我们已经使用表单的提交指定的URL
/rango/add_category/
。所以我们需要效仿
rango/url.py
和修改
urlpattern
如下。

urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^about/$', views.about, name='about'),
url(r'^add_category/$', views.add_category, name='add_category'), # NEW MAPPING!
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),)


在这个案例中排序不是必须的。然而,看看官方Django文档Django如何处理请求更多的信息。我们的新URL添加类别
/rango/add_category/


修改Index页面视图

作为最后一步把索引页上的链接,这样我们可以很容易地添加类别。编辑模板
rango/index.html
,然后在
</ body>
结束标记之前添加以下HTML超链接。

<a href="/rango/add_category/">Add a New Category</a><br />


Demo

现在,让我们来试试!运行Django开发服务器,并导航到http://127.0.0.1:8000/rango/。使用你的新链接跳转到添加类别页面,并添加一个类别。图1显示的截图添加的目录和索引页面。



Figure 1: Adding a new category to Rango with our new form. The diagram illustrates the steps involved.

注释

如果你添加的类别,他们不会总是出现在索引页上,这是因为我们只显示索引页上的前5个类别。如果你登录到管理界面应该能够查看所有的类别。去看发生了什么当你进入
add_category()
方法的
rango/view.py
,您可以对类别模型对象的引用从
form.save()
创建,
cat = form.save(commit=True)
然后打印到控制台的类别和slug,
print cat,cat.slug
看看什么会被创建。

清理表单

回想一下,我们的
page
模型设置为
URLField
类型实例的
url
属性。在相应的HTML表单,Django合理期望任何文本输入一个
url
字段是一个格式良好的,完整的
url
。然而,用户可以找到进入类似http://www.url.com很麻烦——事实上,用户甚至不知道正确的URL形式!

在用户输入的场景可能不完全正确,我们可以覆盖
ModelForm
实现的
clean()
方法。这种方法要求表单数据保存到一个新的模式实例之前,从而为我们提供了一个合乎逻辑的地方插入代码,可以验证 - 甚至修复 - 任何形式的数据的用户输入。在上面的例子中,我们可以检查用户输入的
url
字段的值从
http://
开始,如果没有,我们可以预先考虑
http://
用户的输入。

class PageForm(forms.ModelForm):

...

def clean(self):
cleaned_data = self.cleaned_data
url = cleaned_data.get('url')

# If url is not empty and doesn't start with 'http://', prepend 'http://'.
if url and not url.startswith('http://'):
url = 'http://' + url
cleaned_data['url'] = url

return cleaned_data


clean()
方法中,一个简单的模式观察,你可以在自己的Django表单处理代码复制。

ModelForm
字典获取表单数据的属性
cleaned_data


你要检查表单字段就可以取自
cleaned_data
字典。使用的字典对象提供的
.get()
方法来获取表单的值。如果用户没有输入一个值到一个表单字段,它的条目不存在
cleaned_data
字典。在这个实例中,
. get()
将返回
None
而不是抛出
KeyError
。这有助于你的代码看起来干净一点。

对于要处理的每个表单字段,检查一个值被检索。如果输入某些值,检查值是什么。如果不是你所期望的,你可以再添加一些逻辑,给定字段重新分配值在
cleaned_data
字典之前解决这个问题。

你必须始终以
clean()
方法结束通过放回引用
clean_data
字典。如果你不这样做,你会得到一些真正令人沮丧的错误!

这个简单的例子显示了我们如何能够通过清洗的表单被储存前的数据传递。这是非常方便的,尤其是在指定的字段需要有默认值 - 或者表单中的数据丢失,,我们需要处理这些数据的输入问题。

注释

重写方法实现为Django框架的一部分可以为您提供一种优雅的方式来为您的应用程序添加额外的功能。有许多方法可以放心地覆盖有好处,就像在
ModelForm
中的
clean()
方法,如上图所示。看看官方的Django文档模型更多的例子如何覆盖缺省功能槽你自己的。

练习

现在,你已经通过了一章的工作,请尝试以下练习来巩固你的Django的表单功能方面的知识。

当你不能在添加分类表单中输入分类名称是会发生什么?

当你尝试添加一个已经存在的分类时会发生什么?

当你访问一个不存在的分类时会发生什么?

当用户访问一个不存在的类你如何能妥善地处理?

承接Django官方教程的第四部分,如果你还没有这样做的话,以加强你在这儿学到。

创建一个Add Page视图,模版和URL映射

下一步将允许用户将页面添加到给定的类别。为此,重复相同的工作流程页面上方——创建一个新视图(
add_page()
),一个新的模板(
rango/add_page.html
),URL映射,然后添加一个链接的目录页。在你开始之前,这里有视图逻辑。

from rango.forms import PageForm

def add_page(request, category_name_slug):

try:
cat = Category.objects.get(slug=category_name_slug)
except Category.DoesNotExist:
cat = None

if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if cat:
page = form.save(commit=False)
page.category = cat
page.views = 0
page.save()
# probably better to use a redirect here.
return category(request, category_name_slug)
else:
print form.errors
else:
form = PageForm()

context_dict = {'form':form, 'category': cat}

return render(request, 'rango/add_page.html', context_dict)


提示

与上面的练习帮助你,以下提示可能对你有用。

更新
Category()
视图通过
category_name_slug
通过插入视图的
context_dict
字典。

更新
category.html
连接到
/rango/category/<category_name_url>/add_page/


确保链接只出现在请求的目录存在,有或没有页面。模板中检查
{% if category %} .... {% else %} A category by this name does not exist {% endif %}


更新
rango/url.py
和一个URL映射来处理上述链接。

原文: http://www.tangowithdjango.com/book17/chapters/forms.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  django 表单