我们将继续part2中的polls应用,继续创建对外接口views
。
1 概述
一个views是django应用中web页面的一个“类型”,一般服务于一个特定函数和一个特定的模板,举个例子,在一个blog的应用中,可能拥有下面几个views:
- blog主页,显示一些最新的文章入口
- “detail”页面入口,用于每片文章的永久链接
- 年归档页面,给定年份的每个月入口
- 月归档页面,给定月的每日入口
- 日归档页面,给定天的文章入口
- 评论,处理提交评论到指定的入口
在我们的polls应用中,我们需要下面的几个views:
- Question “index”页面,显示最新的几个问题
- Question “detail” 页面,显示问题内容,不显示结果但是有一个投票的form表单提交框
- Question “results” 页面,显示特定问题的投票结果
- vote动作,处理投票,特定的选择绑定到特定的问题上
在django中,web页面和其他内容都是有views实现的。每个view相当与一个简单的python函数(如果是基于class的话就是类中的一个方法)。django通过遍历url来选择view。
django提供了更优雅的url,如/newsarchive/<year>/<month>/
,而不是ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B
。
要从url得到view,django使用了URLconfs,一个URLconf指向了一个URL样式(使用正则表达式编写)到一个view。
2 编写更多的view
修改polls/views.py
文件:
from django.shortcuts import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
def detail(request,question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request,question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request,question_id):
return HttpResponse("You're voting on question %s." % question_id)
修改polls/urls.py
文件:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$',views.index,name="index"),
url(r'^(?P<question_id>[0-9]+)/$',views.detail,name="detail"),
url(r'^(?P<question_id>[0-9]+)/results/$',views.results,name="results"),
url(r'^(?P<question_id>[0-9]+)/vote/$',views.vote,name="vote"),
]
打开浏览器,/polls/34/
将运行detail()
,当有人请求页面的时候,说“/polls/34/ ”,django将会加载“mysite.urls ”,因为在settings里面的“ROOT_URLCONF”的设置。它会按顺序找到“urlpatterns ”参数对应的正则表达式匹配的url,首先匹配到’^polls/’, 然后到‘polls.urls’中匹配到"34/" – r’^(?P<question_id>[0-9]+)/$’, 并将url中的参数映射给 detail() view:
detail(request=<HttpRequest object>, question_id='34')
(?P<question_id>[0-9]+)
中的question_id将作为参数传给view中的函数,[0-9]+
表示一个或多个数字。当然,也可以直接使用html来作为url,如下:
url(r'^polls/latest.html$', views.index),
但是,不要这么弄,这样太傻了。
3 写view到底干了啥
每一个view负责做下面两件事情中的一件:
-
返回一个包含内容的HttpResponse 对象
-
返回一个例外,如http404
你的view能够读取数据库中的记录,能够使用django的template系统或第三方模板系统,能够生成一个pdf文件,输出xml,创建一个zip文件,通过使用python的库得到你所想要的任何东西。
django需要的只是HttpResponse 对象,现在我们来修改一下part2中的index(),用来列举最近的5个问题。
#_*_coding:utf-8_*_
from django.shortcuts import HttpResponse
from . import models
def index(request):
#倒排所有的question问题,只取前五个
latest_question_list = models.Question.objects.order_by("-pub_date")[:5]
#合并列表,中间用","隔开
output = ', '.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
但是有一个问题:view使用硬编码(hard-coded)方式编写,如果你想要修改页面就需要修改python代码,所以django使用template模板系统来显示页面。
首先创建一templates的目录在polls下面,django会在这里边找模板,你的工程中TEMPLATES 设置描述了django如何加载和渲染模板, describes how Django will load and render templates. 默认设置一个 DjangoTemplates backend 中 APP_DIRS 为True. DjangoTemplates 将会在每个INSTALLED_APPS中寻找 “templates” 。
在template下再创建一个polls的文件夹,以后可以通过polls/index.html来访问模板文件。
Template的命名
我们可以将所有的html文件都放置在一起,但是不方便。如果不同的app有相同的模板文件,django是不能分辨他们的,一个比较好的办法是将不同的模板放置在以app命名的文件夹中。
修改 polls/templates/polls/index.html
文件:
...
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
...
修改views文件
#_*_coding:utf-8_*_
from django.shortcuts import HttpResponse
from . import models
from django.template import loader
def index(request):
#倒排所有的question问题,只取前五个
latest_question_list = models.Question.objects.order_by("-pub_date")[:5]
#得到一个模板
temp = loader.get_template('polls/index.html')
#需要传入的内容,一个字典
context = {
"latest_qiestion_list":latest_question_list,
}
#返回
return HttpResponse(temp.render(context,request))
...
A shortcut: render()
加载和渲染模板是非常频繁的工作,如果每次使用HttpResponse
来返回,比较麻黄,因此django提供了一个叫做render()
的捷径:
#_*_coding:utf-8_*_
from django.shortcuts import HttpResponse,render
from . import models
def index(request):
#倒排所有的question问题,只取前五个
latest_question_list = models.Question.objects.order_by("-pub_date")[:5]
#返回
return render(request,"polls/index.html",{
"latest_question_list":latest_question_list,
})
...
这样就不用导入loader
了。render() 函数的第一个参数是request,第二个参数是模板,第三个参数是一个字典,用于模板传入参数使用, 返回一个给定内容和渲染模板的 HttpResponse 对象。
4 Raising a 404 error
下面来处理下detail视图,显示问题内容,修改polls/views.py
文件:
...
def detail(request,question_id):
try:
question = models.Question.objects.get(pk=question_id)
except models.Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request,"polls/detail.html",{
"question":question
})
...
修改polls/templates/polls/detail.html文件,如下:
{{ question }}
A shortcut: get_object_or_404()
我们经常get一个对象数据,如果不存在就产生一个HTTP404,django提供了一个快捷方式:
...
def detail(request,question_id):
question = get_object_or_404(models.Question,pk=question_id)
return render(request,"polls/detail.html",{
"question":question
})
...
Philosophy :
为什么我们使用get_object_or_404() 或者Http404 来代替ObjectDoesNotExist 呢?
因为我们需要联系models层和views层,django的一个重要设计理念就是维持松耦合,一些controlled 耦合 在django.shortcuts模块中介绍。
还有一个get_list_or_404() 函数,对应的filter(),而get_object_or_404() 对应的是get()。
5 使用模板系统(template)
回到detail()视图,我们修改一下detail.html文件:
<h1>
{{ question.question_text }}
</h1>
<ul>
{#question.choice_set.all:全部choice#}
{% for choice in question.choice_set.all %}
{# 显示choice的内容:#}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
通过“.”来获取question表对应的列,如question.question_text获取了question的内容;同样也能使用for进行循环,注意格式。
6 删除模板中url的硬编码
还记得index.html中的:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
我们给他改成:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
其实他是通过一个{% url %} 的template tag来实现调用的,详细请参考tempalte tag。
那么如果哪天我需要修改url的时候,如:
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
就不需要再调整模板中的index.html文件。
7 url的命名
在我们的例子中只有一个app就是polls,但是在真实的django中,我们会有五个、十个甚至更多的app,怎么区分它们的url名字呢?比如说bolg和polls都有detail视图,那么使用{% url %}如何区分不同的app,方法就是在url文件中增加一行app_name = 'polls'
。
from django.conf.urls import url
from . import views
app_name = "polls" #增加此行
urlpatterns = [
url(r'^$',views.index,name="index"),
url(r'^(?P<question_id>[0-9]+)/$',views.detail,name="detail"),
url(r'^(?P<question_id>[0-9]+)/results/$',views.results,name="results"),
url(r'^(?P<question_id>[0-9]+)/vote/$',views.vote,name="vote"),
]
原来调用的时候:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
现在这样:polls:detail
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
未完待续。