[译]Django first steps Part3
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>
未完待续。