一个简单的表单处理示例
继续本书一直进行的关于书籍、作者、出版社的例子,我们现在来创建一个简单的view函数以便让用户可以通过书名从数据库中查找书籍。
通常,表单开发分为两个部分: 前端HTML页面用户接口和后台view
函数对所提交数据的处理过程。第一部分很简单;现在我们来建立个view
来显示一个搜索表单:
from django.shortcuts import render_to_response
def search_form(request):
return render_to_response('search_form.html')
在第三章已经学过,这个view
函数可以放到Python的搜索路径的任何位置。 为了便于讨论,咱们将它放在books/views.py
里。
这个 search_form.html
模板,可能看起来是这样的:
<html>
<head>
<title>Search</title>
</head>
<body>
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
而 urls.py
中的 URLpattern
可能是这样的:
from mysite.books import views
urlpatterns = patterns('',
# ...
(r'^search-form/$', views.search_form),
# ...
)
(注意,我们直接将views
模块import
进来了,而不是用类似from mysite.views import search_form
这样的语句,因为前者看起来更简洁。我们将在第8章讲述更多的关于import的用法。)
现在,如果你运行 runserver
命令,然后访问http://127.0.0.1:8000/search-form/
,你会看到搜索界面。非常简单。
不过,当你通过这个form提交数据时,你会得到一个Django 404错误。这个Form指向的URL /search/ 还没有被实现。让我们添加第二个视图函数并设置URL:
# urls.py
urlpatterns = patterns('',
# ...
(r'^search-form/$', views.search_form),
(r'^search/$', views.search),
# ...
)
# views.py
def search(request):
if 'q' in request.GET:
message = 'You searched for: %r' % request.GET['q']
else:
message = 'You submitted an empty form.'
return HttpResponse(message)
暂时先只显示用户搜索的字词,以确定搜索数据被正确地提交给了Django,这样你就会知道搜索数据是如何在这个系统中传递的。简而言之:
- 在HTML里我们定义了一个变量
q
。当提交表单时,变量q
的值通过GET(method=”get”)
附加在URL /search/
上。 - 处理
/search/(search())
的视图通过request.GET
来获取q
的值。
需要注意的是在这里明确地判断q
是否包含在request.GET
中。就像上面request.META
小节里面提到,对于用户提交过来的数据,甚至是正确的数据,都需要进行过滤。在这里若没有进行检测,那么用户提交一个空的表单将引发KeyError异常:
# BAD!
def bad_search(request):
# The following line will raise KeyError if 'q' hasn't
# been submitted!
message = 'You searched for: %r' % request.GET['q']
return HttpResponse(message)
查询字符串参数
因为使用GET方法的数据是通过查询字符串的方式传递的(例如/search/?q=django),所以我们可以使用requet.GET
来获取这些数据。第三章介绍Django的URLconf
系统时我们比较了Django的简洁的URL与PHP/Java传统的URL,我们提到将在第七章讲述如何使用传统的URL。通过刚才的介绍,我们知道在视图里可以使用request
.GET来获取传统URL里的查询字符串(例如hours=3)。
获取使用POST方法的数据与GET的相似,只是使用request.POST
代替了request.GET
。那么,POST
与GET
之间有什么不同?当我们提交表单仅仅需要获取数据时就可以用GET
; 而当我们提交表单时需要更改服务器数据的状态,或者说发送e-mail,或者其他不仅仅是获取并显示数据的时候就使用POST
。在这个搜索书籍的例子里,我们使用GET
,因为这个查询不会更改服务器数据的状态。 (如果你有兴趣了解更多关于GET
和POST
的知识,可以参见http://www.w3.org/2001/tag/doc/whenToUseGet.html。)
既然已经确认用户所提交的数据是有效的,那么接下来就可以从数据库中查询这个有效的数据(同样,在views.py
里操作):
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
else:
return HttpResponse('Please submit a search term.')
让我们来分析一下上面的代码:
除了检查
q
是否存在于request.GET
之外,我们还检查来自reuqest.GET[‘q’]
的值是否为空。我们使用
Book.objects.filter(title__icontains=q)
获取数据库中标题包含q
的书籍。icontains
是一个查询关键字(参看第五章和附录B)这个语句可以理解为获取标题里包含q的书籍,不区分大小写。这是实现书籍查询的一个很简单的方法。我们不推荐在一个包含大量产品的数据库中使用
icontains
查询,因为那会很慢。(在真实的案例中,我们可以使用以某种分类的自定义查询系统。在网上搜索“开源全文搜索”看看是否有好的方法)最后,我们给模板传递来
books
,一个包含Book
对象的列表。 查询结果的显示模板search_results.html
如下所示:
<p>You searched for: <strong>{{ query }}</strong></p>
{% if books %}
<p>Found {{ books|length }} book{{ books|pluralize }}.</p>
<ul>
{% for book in books %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>No books matched your search criteria.</p>
{% endif %}
注意这里
pluralize
的使用,这个过滤器在适当的时候会输出s
(例如找到多本书籍)。
{$ activeFileHint $}