Python的Django框架中的表单处理示例
组建一个关于书籍、作者、出版社的例子:
fromdjango.dbimportmodels classPublisher(models.Model): name=models.CharField(max_length=30) address=models.CharField(max_length=50) city=models.CharField(max_length=60) state_province=models.CharField(max_length=30) country=models.CharField(max_length=50) website=models.URLField() classAuthor(models.Model): first_name=models.CharField(max_length=30) last_name=models.CharField(max_length=40) email=models.EmailField() classBook(models.Model): title=models.CharField(max_length=100) authors=models.ManyToManyField(Author) publisher=models.ForeignKey(Publisher) publication_date=models.DateField()
我们现在来创建一个简单的view函数以便让用户可以通过书名从数据库中查找书籍。
通常,表单开发分为两个部分:前端HTML页面用户接口和后台view函数对所提交数据的处理过程。第一部分很简单;现在我们来建立个view来显示一个搜索表单:
fromdjango.shortcutsimportrender_to_response defsearch_form(request): returnrender_to_response('search_form.html')
这个view函数可以放到Python的搜索路径的任何位置。为了便于讨论,咱们将它放在books/views.py里。
这个search_form.html模板,可能看起来是这样的:
<html> <head> <title>Search</title> </head> <body> <formaction="/search/"method="get"> <inputtype="text"name="q"> <inputtype="submit"value="Search"> </form> </body> </html>
而urls.py中的URLpattern可能是这样的:
frommysite.booksimportviews urlpatterns=patterns('', #... (r'^search-form/$',views.search_form), #... )
(注意,我们直接将views模块import进来了,而不是用类似frommysite.viewsimportsearch_form这样的语句,因为前者看起来更简洁。)
现在,如果你运行runserver命令,然后访问http://127.0.0.1:8000/search-form/,你会看到搜索界面。非常简单。
不过,当你通过这个form提交数据时,你会得到一个Django404错误。这个Form指向的URL/search/还没有被实现。让我们添加第二个视图函数并设置URL:
#urls.py urlpatterns=patterns('', #... (r'^search-form/$',views.search_form), (r'^search/$',views.search), #... ) #views.py defsearch(request): if'q'inrequest.GET: message='Yousearchedfor:%r'%request.GET['q'] else: message='Yousubmittedanemptyform.' returnHttpResponse(message)
暂时先只显示用户搜索的字词,以确定搜索数据被正确地提交给了Django,这样你就会知道搜索数据是如何在这个系统中传递的。简而言之:
在HTML里我们定义了一个变量q。当提交表单时,变量q的值通过GET(method=”get”)附加在URL/search/上。
处理/search/(search())的视图通过request.GET来获取q的值。
需要注意的是在这里明确地判断q是否包含在request.GET中。就像上面request.META小节里面提到,对于用户提交过来的数据,甚至是正确的数据,都需要进行过滤。在这里若没有进行检测,那么用户提交一个空的表单将引发KeyError异常:
#BAD! defbad_search(request): #ThefollowinglinewillraiseKeyErrorif'q'hasn't #beensubmitted! message='Yousearchedfor:%r'%request.GET['q'] returnHttpResponse(message)
查询字符串参数
因为使用GET方法的数据是通过查询字符串的方式传递的(例如/search/?q=django),所以我们可以使用requet.GET来获取这些数据。我们知道在视图里可以使用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里操作):
fromdjango.httpimportHttpResponse fromdjango.shortcutsimportrender_to_response frommysite.books.modelsimportBook defsearch(request): if'q'inrequest.GETandrequest.GET['q']: q=request.GET['q'] books=Book.objects.filter(title__icontains=q) returnrender_to_response('search_results.html', {'books':books,'query':q}) else: returnHttpResponse('Pleasesubmitasearchterm.')
让我们来分析一下上面的代码:
- 除了检查q是否存在于request.GET之外,我们还检查来reuqest.GET[‘q']的值是否为空。
- 我们使用Book.objects.filter(title__icontains=q)获取数据库中标题包含q的书籍。icontains是一个查询关键字。这个语句可以理解为获取标题里包含q的书籍,不区分大小写。
- 这是实现书籍查询的一个很简单的方法。我们不推荐在一个包含大量产品的数据库中使用icontains查询,因为那会很慢。(在真实的案例中,我们可以使用以某种分类的自定义查询系统。在网上搜索“开源全文搜索”看看是否有好的方法)
最后,我们给模板传递来books,一个包含Book对象的列表。查询结果的显示模板search_results.html如下所示:
<p>Yousearchedfor:<strong>{{query}}</strong></p> {%ifbooks%} <p>Found{{books|length}}book{{books|pluralize}}.</p> <ul> {%forbookinbooks%} <li>{{book.title}}</li> {%endfor%} </ul> {%else%} <p>Nobooksmatchedyoursearchcriteria.</p> {%endif%}
注意这里pluralize的使用,这个过滤器在适当的时候会输出s(例如找到多本书籍)。