Forms
HTML form是交互网页的支柱。下面来学习一下有关使用Django处理用户提交的表单数据,验证等功能。
我们将讨论HttpRequest和Form对象。
- request中包含的信息
在views.py中的每一个用于显示页面的函数都需要以request作为第一个函数参数。request包含了一些有用
的信息,如:
request.path 除去了域名和端口的访问路径,
request.get_host 域名+端口信息
request.get_full_path() 所有路径,包含传递的参数
requets.is_secure() 是否使用https进行链接
![](https://i-blog.csdnimg.cn/blog_migrate/17edc95406d97d4ed3b92413c7b10e36.png)
![](https://i-blog.csdnimg.cn/blog_migrate/64acbabc111508bf30a209ff9e072e91.png)
还有一个特别的属性request.META,它是一个dict类型,包含了所有HTTP header信息,不过也要根据用户
的实际环境来决定。如
HTTP_REFERER
HTTP_USER_AGENT 用户使用的浏览器信息
REMOTE_ADDR 用户的IP,如果使用代理,也可以显示出来
(有很多属性,只是截图了一些)
![](https://i-blog.csdnimg.cn/blog_migrate/7105925ee77b6f1639f41f4004757953.png)
并不是每一个用户提交的头信息都相同,所以在读取的时候,要考虑一些异常情况,比如取不到相应的值的情况。
可以使用try...except,也可以使用dict.get(key, default)中默认返回为预设值的方法。
def ua_display_good1(request):
try:
ua = request.META['HTTP_USER_AGENT']
except KeyError:
ua = 'unknown'
return HttpResponse("Your browser is %s" % ua)
def ua_display_good2(request):
ua = request.META.get('HTTP_USER_AGENT', 'unknown')
return HttpResponse("Your browser is %s" % ua)
除了以上的这些元数据的信息,HttpRequest对象还包括两个属性包括了用户提交的数据:
request.GET和request.POST。它们都是dictionary-like对象。意思就是说可以像dict那样
操作,但不是dict,还有一些dict没有的操作。
下面构造一个用于用户搜索的简单form例子。
#template/search_form.html
<
form
action=
"/search/"
method=
"get">
< input type= "text" name= "q">
< input type= "submit" value= "Search">
</ form>
< input type= "text" name= "q">
< input type= "submit" value= "Search">
</ form>
#template/search_results.html
<
p>Your searched for '{{ query }}'
{% if books %}
{% for book in books %}
< li>{{ book. title }}</ li>
{% endfor %}
{% else %}
< p>No books found.
{% endif %}
{% if books %}
{% for book in books %}
< li>{{ book. title }}</ li>
{% endfor %}
{% else %}
< p>No books found.
{% endif %}
# urls.py
from django.conf.urls import patterns, include, url
from books.views import hello, search_form, search
urlpatterns = patterns( '',
url(r '^search-form/$', search_form),
url(r '^search/$', search),
)
#views.py
from django.http import HttpResponse
from django.shortcuts import render_to_response
from books.models import Book
def search_form(request) :
return render_to_response( 'search_form.html')
def search(request) :
if 'q' in request.GET : #GET是一个dict,使用文本框的name作为key
#在这里需要做一个判断,是否存在提交数据,以免报错
q = request.GET[ 'q']
#使用lookup后缀,意思为书名不区分大小写,包含q就可以
books = Book.objects. filter(title__icontains =q)
return render_to_response( 'search_results.html', { 'books' : books, 'query' :q})
else :
message = 'You submitted an empty form.'
#只是简单的返回一个response对象,因为没有使用模块,所以也不用渲染数据Context
return HttpResponse(message)
from django.conf.urls import patterns, include, url
from books.views import hello, search_form, search
urlpatterns = patterns( '',
url(r '^search-form/$', search_form),
url(r '^search/$', search),
)
#views.py
from django.http import HttpResponse
from django.shortcuts import render_to_response
from books.models import Book
def search_form(request) :
return render_to_response( 'search_form.html')
def search(request) :
if 'q' in request.GET : #GET是一个dict,使用文本框的name作为key
#在这里需要做一个判断,是否存在提交数据,以免报错
q = request.GET[ 'q']
#使用lookup后缀,意思为书名不区分大小写,包含q就可以
books = Book.objects. filter(title__icontains =q)
return render_to_response( 'search_results.html', { 'books' : books, 'query' :q})
else :
message = 'You submitted an empty form.'
#只是简单的返回一个response对象,因为没有使用模块,所以也不用渲染数据Context
return HttpResponse(message)
![](https://i-blog.csdnimg.cn/blog_migrate/6313c14518015d493ca5787982123f00.png)
还有一点,form中的action属性如果为空的,代表提交数据到当前页面。
关于表单验证
可以在server端进行,有时会加重server负担,也可以使用Javascript在client端进行。有一点需要注意的是,
即使你使用了javascript在client进行了验证,还是需要在server端对用户提交的数据进行审查,比如有些人
会在浏览器中禁用javascript,也有些人会来搞注入攻击。总之,不能相信用户提交的提交,进行防御性编码。
关于使用重定向
你应当总是在一个成功处理POST请求之后进行redirect,这样可以避免用户多次点击提交按钮带来的二次请求
问题,有时会造成不好的后果,如向数据库插入重复记录等。
from django.http import HttpResponseRedirect
return HttpResponseRedirect('/success/')
- 使用Django的form system来简化表单工作
在app文件夹下创建forms.py,其实在哪里创建都可以:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField()
form的定义和model类的定义很像。我们可以使用python manage.py shell来查看form类的信息。
![](https://i-blog.csdnimg.cn/blog_migrate/6e1ef580d5e57ad199b99f2dbb2f3404.png)
可以看到打印出来的f的信息其实是网页格式的。
也可以改变默认表格的格式。
![](https://i-blog.csdnimg.cn/blog_migrate/2f06451385cc239134345a6378a8891c.png)
这里的<table>,<ul>都没有包含在输出里,所以可以自由地添加行等。
除了打印出整个form的HTML内容,还可以打印出局的字段的内容。
![](https://i-blog.csdnimg.cn/blog_migrate/4459f2c5529c962d9e21845307c3bac6.png)
还可以使用form类进行验证,使用dict对form类进行初始化。
>>> f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nice'})
>>> f.is_bound #是否绑定表单类
True
>>> f.is_valid() #数据内容是否合法
True
当数据全部合法时,可以使用clean_data这个属性,用来得到经过'clean'格式化的数据,会所提交过
来的数据转化成合适的Python的类型。
![](https://i-blog.csdnimg.cn/blog_migrate/3c4b4379536763ba151dd67e1fb0a7a7.png)
如何在view中使用form类
# views.py
from django.shortcuts import render_to_response
from mysite.contact.forms import ContactForm
def contact(request) :
from django.shortcuts import render_to_response
from mysite.contact.forms import ContactForm
def contact(request) :
#可以不写,因为python中if中定义的变量,也可以在整个函数中可见
form
=
None
if request.method == 'POST' :
form = ContactForm(request.POST)
if form.is_valid() :
cd = form.cleaned_data
send_mail(
cd[ 'subject'],
cd[ 'message'],
cd.get( 'email', 'noreply@example.com'),
[ 'siteowner@example.com'],
)
return HttpResponseRedirect( '/contact/thanks/') #需要在urls.py中配置路径
else :
form = ContactForm()
return render_to_response( 'contact_form.html', { 'form' : form})
if request.method == 'POST' :
form = ContactForm(request.POST)
if form.is_valid() :
cd = form.cleaned_data
send_mail(
cd[ 'subject'],
cd[ 'message'],
cd.get( 'email', 'noreply@example.com'),
[ 'siteowner@example.com'],
)
return HttpResponseRedirect( '/contact/thanks/') #需要在urls.py中配置路径
else :
form = ContactForm()
return render_to_response( 'contact_form.html', { 'form' : form})
# contact_form.html
<
html>
< head>
< title>Contact us</ title>
</ head>
< body>
< h1>Contact us</ h1>
{% if form.errors %}
< p style= "color: red;">
< head>
< title>Contact us</ title>
</ head>
< body>
< h1>Contact us</ h1>
{% if form.errors %}
< p style= "color: red;">
{# pulralize 是filter,用来判断是否为添加's'来表示单词的复数形式#}
Please correct the error{{ form.errors|pluralize }} below.
</ p>
{% endif %}
< form action= "" method= "post">
< table>
{# 可以使用as_ul, as_p来改变格式 #}
{{ form.as_table }}
</ table>
< input type= "submit" value= "Submit">
</ form>
</ body>
</ html>
Please correct the error{{ form.errors|pluralize }} below.
</ p>
{% endif %}
< form action= "" method= "post">
< table>
{# 可以使用as_ul, as_p来改变格式 #}
{{ form.as_table }}
</ table>
< input type= "submit" value= "Submit">
</ form>
</ body>
</ html>
![](https://i-blog.csdnimg.cn/blog_migrate/98785f847aa92ce58669eb85d10ae164.png)
在构造form的时候,还可以改变字段的构造参数
class ContactForm(forms.Form)
:
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
还可以给form类添加默认值
form = ContactForm(
initial={'subject': 'I love your site!'}
)
给form类自定义验证规则,如果想要重用验证机制,可以单独创建新的字段类,重新写它的验证方法。
一般的可以直接在form类加入clean_字段名的方法,Django会自动查找以clean_开头的函数名,并会在
验证该字段的时候,运行这个函数。
from django
import forms
class ContactForm(forms.Form) :
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
def clean_message( self) :
message = self.cleaned_data[ 'message']
num_words = len(message.split())
if num_words < 4 :
raise forms.ValidationError( "Not enough words!")
return message
class ContactForm(forms.Form) :
subject = forms.CharField(max_length = 100)
email = forms.EmailField(required = False)
message = forms.CharField(widget =forms.Textarea)
def clean_message( self) :
message = self.cleaned_data[ 'message']
num_words = len(message.split())
if num_words < 4 :
raise forms.ValidationError( "Not enough words!")
return message
在自定义的验证函数中,我们必须显示的返回字段名的内容,否则会带来表单数据丢失。
可以改变字段的label显示的名称
email = forms.EmailField(required=False, label='Your e-mail address')