自己动手写Django app,第三部分【完】
来源:互联网 发布:程序员都有什么app 编辑:程序博客网 时间:2024/06/06 17:59
原文地址:https://docs.djangoproject.com/en/1.4/intro/tutorial03/
这个教程是从教程2留下的地方开始的。我们继续这个基于网络调查应用程序并且将关注与创建一个公共接口——“view”。
一、哲学
一个视图是你Django应用程序中(一种特别的功能和一个特别的模板)“一种”网页。比如说,在一个网络博客应用程序中,你可能有下面的视图:
博客页面——显示最近一些记录;
进入“细节”页面——单个记录的永久链接;
基于年存档的页面——显示给定年份带有记录的所有月;
基于月存档的页面——显示给定月份带有记录的所有日;
基于日存档的页面——显示给定日期的所有记录;
评论动作——处理给定记录的留言;
在我们的调查应用程序中,我们有下面的四个视图:
调查“索引”页面——显示最近的一些调查;
调查“细节”页面——显示一个没有结果但是带有投票的表单的调查问题;
调查“结果”页面——显示某一个调查的结果;
投票动作——处理某一个调查的某一个投票;
在Django中,每个视图由一个简单的python函数表示。
二、设计你的URLs
写视图的第一步是设计你的URL结构。你通过创建一个叫URLconf的python模块来实现它。URLconfs是Django把给定的URL和一个给定的python代码联系起来。
当一个用户要求一个Django支持的页面,系统查看ROOT_URLCONF设置,它包含了一个python点缀语法的字符串。Django载入那个模块然后查找一个叫urlpatterns的模块层变量,它是下列各式的一列元组:
1
(regular expression, Python callback function [, optional dictionary])
当你找到匹配的哪个是,Django调用一个带有HttpResponse对象作为第一个参数的python回调函数,正则表达式中任何“捕获”的值都作为关键字参数,并且任意关键字参数都可以来自字典(一个元组中可选的第三个参数)。
关于更多关于HttpResponse对象的信息,请查看请求和响应对象。更多关于URLconfs,请查看URL分配器。
当你在教程1开头的地方运行django-admin.pu startproject mysite,系统在mysite/urls.py中创建了一个默认的URLconf。它也自动把你的ROOT_URLCONF设置(在setting.py中)指到那个文件:
1
ROOT_URLCONF
=
'mysite.urls'
01
from
django.conf.urls
import
patterns, include, url
02
03
from
django.contrib
import
admin
04
admin.autodiscover()
05
06
urlpatterns
=
patterns('',
07
url(r
'^polls/$'
,
'polls.views.index'
),
08
url(r
'^polls/(?P<poll_id>\d+)/$'
,
'polls.views.detail'
),
09
url(r
'^polls/(?P<poll_id>\d+)/results/$'
,
'polls.views.results'
),
10
url(r
'^polls/(?P<poll_id>\d+)/vote/$'
,
'polls.views.vote'
),
11
url(r
'^admin/'
, include(admin.site.urls)),
12
)
1
detail(request
=
<HttpRequest
object
>, poll_id
=
'23'
)
因为URL模式是一个正则表达式,你对它们做的一切都没有限制。因此这里没有必要增加像.php这样的URL令人讨厌的东西——除非你有一个与众不同的幽默感,这种情况下,你可以这样做:
1
(r
'^polls/latest\.php$'
,
'polls.views.index'
),
注意这些正则表达式不搜索GET和POST参数,或者域名。比如说,一个请求:http://www.example.com/myapp/,URLconf将会查找myapp/,在一个请求:http://example.com/myapp/?page=3,URLconf将会查找myapp/。
如果在正则表达式方面需要帮助,请查看维基百科的介绍和re模块的文档。同时,Jeffrey Friedl写的O'Reilly出版的书“掌握正则表达式”也非常精彩。
最后,一个性能注意:这些正则表达式是在你URLconf模块第一次载入的时候编译的,它们的速度是非常快的。
三、写你自己的视图
不过到目前为止我们还没有创建自己的视图——我们只是有URLconf。但是我们让Django来合适的遵循URLconf。
启动Django开发网络服务器:python manage.py runserver。
现在在你的浏览器中浏览“http://localhost:8000/polls/”。你应该看到下面带有鲜艳颜色错误的页面:
1
ViewDoesNotExist at
/
polls
/
2
3
Could
not
import
polls.views.index. View does
not
exist
in
module polls.views.
尝试“/polls/23/”,“/polls/23/results/”和“/polls/23/vote/”都一样的,错误消息告诉你Django没有找到视图(查找视图失败,因为你还没有写任何视图)。
显示是时候写第一个视图了。打开polls/views.py文件然后输入下面的python代码:
1
from
django.http
import
HttpResponse
2
3
def
index(request):
4
return
HttpResponse(
"Hello, world. You're at the poll index."
)
现在让我们增加一些更多的视图。这些视图有一点不同,因为他们带有一个参数(记住,这是从URLconf中正则表达式捕获的传递进来的):
1
def
detail(request, poll_id):
2
return
HttpResponse(
"You're looking at poll %s."
%
poll_id)
3
4
def
results(request, poll_id):
5
return
HttpResponse(
"You're looking at the results of poll %s."
%
poll_id)
6
7
def
vote(request, poll_id):
8
return
HttpResponse(
"You're voting on poll %s."
%
poll_id)
在你的浏览器中查看“/polls/34/”,它运行detail()方法然手显示你在URL中提供的ID。尝试“/polls/34/results/”和“/polls/34/vote/”也是一样的——它们显示结果和投票页面的占的地方。
四、写一个实际能做一些事情的视图
每个视图都做两件事情中的一件:返回一个包含请求页面内容的HttpResponse对象,或者产生一个错误比如说Http404。剩下的就取决于你。
你的视图可以从数据库读取记录,或者不可以。它可以用一个模板系统比如说Django自带的或者第三方python模板系统,或者不可以。他用你想用的python库可以生成一个PDF文件,输出XML,创建一个动态的ZIP文件,你想的一切都可以。
所有的Django期待的是一个HttpResponse或者是一个溢出。
因为这很方便,让我们用Django自己的数据库API,这个我们在教程1中已经涉及到了。这儿是index()视图的一个尝试,它显示系统中按照公开时间的最近五个用逗号分开的调查问题:
1
from
polls.models
import
Poll
2
from
django.http
import
HttpResponse
3
4
def
index(request):
5
latest_poll_list
=
Poll.objects.
all
().order_by(
'-pub_date'
)[:
5
]
6
output
=
', '
.join([p.question
for
p
in
latest_poll_list])
7
return
HttpResponse(output)
01
from
django.template
import
Context, loader
02
from
polls.models
import
Poll
03
from
django.http
import
HttpResponse
04
05
def
index(request):
06
latest_poll_list
=
Poll.objects.
all
().order_by(
'-pub_date'
)[:
5
]
07
t
=
loader.get_template(
'polls/index.html'
)
08
c
=
Context({
09
'latest_poll_list'
: latest_poll_list,
10
})
11
return
HttpResponse(t.render(c))
重新载入这个页面,现在你会看到一个错误:
1
TemplateDoesNotExist at
/
polls
/
2
polls
/
index.html
当你已经做完了这些,在你的模板目录下创建一个polls目录。在这个目录里面,创建一个叫index.html。注意我们的loader.get_template('polls/index.html')代码从上面映射到系统的“[template_directory]/polls/index.html”。
把下面的代码放到模板里:
1
{
%
if
latest_poll_list
%
}
2
<ul>
3
{
%
for
poll
in
latest_poll_list
%
}
4
<li><a href
=
"/polls/{{ poll.id }}/"
>{{ poll.question }}<
/
a><
/
li>
5
{
%
endfor
%
}
6
<
/
ul>
7
{
%
else
%
}
8
<p>No polls are available.<
/
p>
9
{
%
endif
%
}
五、一个快捷方式:render_to_response()
用提供模板的结果载入一个模板,填写上下文,返回一个HttpResponse对象,这些都是很常见的习语。Django提供一个快捷方式。这是我们的全部重写的index()的视图:
1
from
django.shortcuts
import
render_to_response
2
from
polls.models
import
Poll
3
4
def
index(request):
5
latest_poll_list
=
Poll.objects.
all
().order_by(
'-pub_date'
)[:
5
]
6
return
render_to_response(
'polls/index.html'
, {
'latest_poll_list'
: latest_poll_list})
render_to_response()函数带有一个模板名作为它的第一个参数和一个作为第二个可选参数的字典。它返回一个提供给定上下文的给定模板的HttpResponse对象。
六、产生404错误
现在,我们来处理调查细节的视图——显示给定调查的问题。下面是视图:
1
from
django.http
import
Http404
2
# ...
3
def
detail(request, poll_id):
4
try
:
5
p
=
Poll.objects.get(pk
=
poll_id)
6
except
Poll.DoesNotExist:
7
raise
Http404
8
return
render_to_response(
'polls/detail.html'
, {
'poll'
: p})
我们后面讨论你可以在polls/detail.html放什么,但是如果你想快点让例子运行起来,只要:
1
{{ poll }}
七、一个快捷方式:get_object_or_404()
用get(),视图不存在时产生一个Http404错误时很常见的习语。Django提供一个快捷方式。下面是重写的detail()视图:
1
from
django.shortcuts
import
render_to_response, get_object_or_404
2
# ...
3
def
detail(request, poll_id):
4
p
=
get_object_or_404(Poll, pk
=
poll_id)
5
return
render_to_response(
'polls/detail.html'
, {
'poll'
: p})
(哲学:为什么我们用一个get_object_or_404()帮助函数而不是在更高的层次自动捕获ObjectDoesNotExit溢出或者让模型的API产生Http404而不是ObjectDoesNotExit?因为那样做会耦合模型层和视图层。Django的一个最重要的设计就是实现松耦合。)
这里有一个get_list_or_404()函数,他就像和get_object_or_404()一样工作——除了使用filter()而不是get()。当列表是空的时候就产生Http404错误。
八、写一个404(页面没找到)视图
当你从一个视图产生Http404错误的时候,Django会载入一个致力于处理404错误的特殊视图。它通过在你的根URLconf(只在你的根URLconf下,不管在其他什么地方设置handler404都无效)查找变量handler404,它是一个python点缀语法的字符串——一般URLconf回调函数用相同的格式。一个404视图本身没有什么特别的:它只是一个普通的视图。
一般来说你不需要烦恼写404视图。如果你没有设置handler404,默认情况下使用内建的django.views.defaults.page_not_found()。这种情况下,你任然有一件事要做:在根模板目录创建一个404.html模板。默认的404视图将会调用适用于所有404错误的模板。如果DEBUG被设置成False(在你的设置文件中),并且你没有创建一个404.html文件,就会产生一个Http500。因此记住创建一个404.html。
关于404视图耦合更多的东西:
如果DEBUG被设置成True(在你的设置文件中),你的404视图将永远不会被用到(因此404.html模板永不被提供)因为此时显示的是错误的追溯。
404视图被称为Django在检查URLconf中正则表达式是没有找到匹配。
九、写一个500(服务器错误)视图
类似地,你的根URLconf可以定义一个handler500,防止服务器错误它指向一个视图。当你视图代码有错误时,服务器错误也可能出现。
十、使用模板系统
回到我们调查应用程序的detail()视图。鉴于上下文变量poll,下面是我们“polls/detail.html”模板可能看起来的样子:
1
<h1>{{ poll.question }}<
/
h1>
2
<ul>
3
{
%
for
choice
in
poll.choice_set.
all
%
}
4
<li>{{ choice.choice }}<
/
li>
5
{
%
endfor
%
}
6
<
/
ul>
在{% for %}循环中会发生方法调用:poll.choice_set.all被翻译成python代码poll.choice_set.all(),它返回Choice对象的迭代并且在{% for %}标签中适合使用。更多关于模板的消息请查看模板指导。
十一、简化URLconfs
花点时间来处理视图和模板系统。就像你编辑URLconf一样,你可能注意到这里有点冗余:
1
urlpatterns
=
patterns('',
2
url(r
'^polls/$'
,
'polls.views.index'
),
3
url(r
'^polls/(?P<poll_id>\d+)/$'
,
'polls.views.detail'
),
4
url(r
'^polls/(?P<poll_id>\d+)/results/$'
,
'polls.views.results'
),
5
url(r
'^polls/(?P<poll_id>\d+)/vote/$'
,
'polls.views.vote'
),
6
)
因为这是一个常见的情况,URLconf框架为常见的前缀提供一个快捷方式。你可以分析出常见的前缀并且增加它们作为patterns()的第一个参数,就像下面:
1
urlpatterns
=
patterns(
'polls.views'
,
2
url(r
'^polls/$'
,
'index'
),
3
url(r
'^polls/(?P<poll_id>\d+)/$'
,
'detail'
),
4
url(r
'^polls/(?P<poll_id>\d+)/results/$'
,
'results'
),
5
url(r
'^polls/(?P<poll_id>\d+)/vote/$'
,
'vote'
),
6
)
既然你通常不想一个程序的前缀应用到URLconf的每个回调中,你可以链接多个patterns()。你mysite/urls.py的全部现在应该看起来是这样的:
01
from
django.conf.urls
import
patterns, include, url
02
03
from
django.contrib
import
admin
04
admin.autodiscover()
05
06
urlpatterns
=
patterns(
'polls.views'
,
07
url(r
'^polls/$'
,
'index'
),
08
url(r
'^polls/(?P<poll_id>\d+)/$'
,
'detail'
),
09
url(r
'^polls/(?P<poll_id>\d+)/results/$'
,
'results'
),
10
url(r
'^polls/(?P<poll_id>\d+)/vote/$'
,
'vote'
),
11
)
12
13
urlpatterns
+
=
patterns('',
14
url(r
'^admin/'
, include(admin.site.urls)),
15
)
我们学习他的时候应该花点时间把我们Django项目配置的调查程序URL去耦合。Django的程序是插件式的——也就是说,每个特别地程序应该可以用最小的大惊小怪应用到另一个Django的安装。
多亏了python manage.py startapp创建的严格目录结构,我们的投票程序在这个时候是相当去耦合化的。但是它的一部分是和Django设置耦合的:URLconf。
我们已经在mysite/urls.py里编辑,但是一个程序的URL设计只针对一个应用程序,而不是Django的安装——因此让我们在应用程序目录里把URLs删掉。
把mysite/urls.py拷贝到polls/urls.py,然后改变mysite/urls.py去删除针对调查程序的URLs然后插入一个include(),让它是这样的:
1
from
django.conf.urls
import
patterns, include, url
2
3
from
django.contrib
import
admin
4
admin.autodiscover()
5
6
urlpatterns
=
patterns('',
7
url(r
'^polls/'
, include(
'polls.urls'
)),
8
url(r
'^admin/'
, include(admin.site.urls)),
9
)
当一个用户访问系统的“/polls/34/”会发生下面的事情:
Django会找到一个匹配的'^polls/';
然后Django会去掉匹配的文本(“polls/”)然后把剩下的文本——“34”——传递给'polls.urls'URLconf进行更多的处理;
既然我们已经去耦合了,我们需要删除每行开头的“polls/”,同时也要删除注册管理站点的那一行。你的poll/urls.py文件现在应该看起来是这样的:
1
from
django.conf.urls
import
patterns, url
2
3
urlpatterns
=
patterns(
'polls.views'
,
4
url(r
'^$'
,
'index'
),
5
url(r
'^(?P<poll_id>\d+)/$'
,
'detail'
),
6
url(r
'^(?P<poll_id>\d+)/results/$'
,
'results'
),
7
url(r
'^(?P<poll_id>\d+)/vote/$'
,
'vote'
),
8
)
include()后面的想法和URLconf去耦合是为了是让它容易变成插件和运行的URLs。 调查调查已经在URLconf中,他们可以被放置在“/polls/”下,或者“/fun_polls/”下,或者“/content/polls/”下,或者任何根路径,程序同样能工作。
所有的调查程序关心的是相对路径,而不是绝对路径。
当你对写视图感到合适的话,读教程4学习更多关于处理通用视图。
教程3结束!
- 自己动手写Django app,第三部分【完】
- 自己动手写Django app,第一部分【完】
- 自己动手写Django app,第二部分【完】
- 自己动手写Django app,第四部分【全剧终】
- 自己动手写编译器、链接器-第三章
- Django tutorial(3) 【翻译】编写第一个Django app,第三部分——玩转模型层的API
- 《自己动手设计数据库》第三部分 其他数据库设计事项
- django常用第三方app大全
- [自己动手写操作系统]的学习实践【第三章】
- 《自己动手写操作系统》第三章a/pmtest1.asm
- 《自己动手写操作系统》第三章pmtest2源码解析
- Django 1.0 中文文档-----指导 第三部分 发布视图
- 编写你的第一个Django应用, 第三部分
- Django官方教程(五)【创建你的第一个 Django 项目,第三部分】
- Django tutorial(1)【翻译】编写第一个Django app,第一部分——创建项目
- 自己动手,写个Web服务器(Java版)——第三篇
- 自己动手写操作系统源码剖析——第三章 pmtest4.asm
- 《自己动手写操作系统》源码解析——第三章pmtest5.asm
- android 查看so,jar栈内存使用情况
- 林徽因 - 《人间四月天》
- PHP中用SimpleXMLElement解析xml
- 自己动手写Django app,第二部分【完】
- hdu4618Palindrome Sub-Array(乱搞)
- 自己动手写Django app,第三部分【完】
- UVa 568: Just the facts
- UITableView 使用小结
- XFire实现WebService一:使用XfireConfigurableServlet
- 自己动手写Django app,第四部分【全剧终】
- 反向键索引研究
- 解决SurfaceView设置透明造成覆盖其他组件的替代方案
- 杭电多校联合第一场hdu4606 occupy cities
- hdu 1254 推箱子