Part 3:视图和模板--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程
所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址。
一、概述
一个视图就是一个页面,通常提供特定的功能,使用特定的模板。例如:在一个博客应用中,你可能会看到下列视图:
- 博客主页:显示最新发布的一些内容
- 每篇博客的详细页面:博客的永久链接
- 基于年的博客页面:显示指定年内的所有博客文章
- 基于月的博客页面:显示指定月内的所有博客文章
- 基于天的博客页面:显示指定日内的所有博客文章
- 发布评论:处理针对某篇博客发布的评论
在我们的投票应用中,我们将建立下面的视图:
- 问卷“index”页:显示最新的一些问卷
- 问卷“detail”页面:显示一个问卷的详细文本内容,没有调查结果但是有一个投票或调查表单。
- 问卷“results”页面:显示某个问卷的投票或调查结果。
- 投票动作页面:处理针对某个问卷的某个选项的投票动作。
在Django中,网页和其它的一些内容都是通过视图来处理的。视图其实就是一个简单的Python函数(在基于类的视图中称为方法)。Django通过对比请求的URL地址来选择对应的视图。
二、编写视图
下面,打开polls/views.py文件,输入下列代码:
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文件中加入下面的url模式,将其映射到我们上面新增的视图。
from django.conf.urls import url
from . import views
urlpatterns = [
# ex: /polls/
url(r'^$', views.index, name='index'),
# ex: /polls/5/
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
# ex: /polls/5/results/
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
# ex: /polls/5/vote/
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
现在去浏览器中访问/polls/34/(注意:这里省略了域名。另外,使用了二级路由后,url中都要添加polls部分,参考前面的章节),它将调用detail()函数,然后在页面中显示你在url里提供的ID。访问/polls/34/results/和/polls/34/vote/,将分别显示预定义的伪结果和投票页面。(PS:这里就不贴图了,请大家务必自己动手测试,多实践。)
上面访问的路由过程如下:当有人访问/polls/34/地址时,Django将首先加载mysite.urls模块,因为它是settings文件里设置的根URL配置文件。在该文件里,Django发现了urlpatterns变量,于是在其内按顺序进行匹配。当它匹配上了^polls/,就裁去url中匹配的文本polls/,然后将剩下的文本“34/”,传递给polls.urls进行下一步的处理。在polls.urls中,又匹配到了r’^(?P<question_id>[0-9]+)/$’,最终结果就是调用该模式对应的detail()视图,也就是下面的函数:
detail(request=<HttpRequest object>, question_id='34')
函数中的question_id=’34’参数,是由(?P<question_id>[0-9]+)而来。在正则表达式中通过一个双圆括号,Django会捕获它匹配到的值并传递给对应的视图,作为视图的位置参数之一,而?P<question_id>则表示我要给这个捕获的值指定一个特殊的变量名,在视图中可以通过question_id这个变量名随意的引用它,形成一个关键字参数,不用考虑参数的位置。至于[0-9]+则是一个很简单的原生正则表达式,用于匹配一系列连续的数字,它匹配到的值也就是具体要传递的参数值。
所有的URL模式都是正则表达式,Django不限制你在url模式中的书写方式。但是,你真的没必要书写一个如下的较为愚蠢的包含.html的模式,它显然是没必要,不够简练的:
url(r'^polls/latest\.html$', views.index),
你完全可以用下面的模式代替上面的:
url(r'^polls/latest$', views.index),
三、编写能实际干点活的视图
每个视图至少做两件事之一:返回一个包含请求页面的HttpResponse对象或者弹出一个类似Http404的异常。其它的则随你便,你爱干嘛干嘛。
下面是一个新的index()视图,用于替代先前无用的index,它会根据发布日期显示最近的5个投票问卷。
from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
# 下面是那些没改动过的视图(detail, results, vote)
这里有个非常重要的问题:在当前视图中的HTML页面是硬编码的。如果你想改变页面的显示内容,就必须修改这里的Python代码。为了解决这个问题,需要使用Django提供的模板系统,解耦视图和模板之间的硬连接。
首先,在polls目录下创建一个新的templates目录,Django会在它里面查找模板文件。在templates目录中,再创建一个新的子目录名叫polls,进入该子目录,创建一个新的html文件index.html。换句话说,你的模板文件应该是polls/templates/polls/index.html。可以在DJango中直接使用polls/index.html引用该文件。
注意:在Pycharm中,templates文件夹通常已经帮你创建好了!
模板命名空间:
你也许会想,为什么不把模板文件直接放在polls/templates目录下,而是费劲的再建个子目录polls呢?设想这么个情况,有另外一个app,它也有一个名叫index.html的文件,当Django在搜索模板时,有可能就找到它,然后退出搜索,这就命中了错误的目标,不是我们想要的结果。解决这个问题的最好办法就是在templates目录下再建立一个与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 %}
同时,修改视图文件polls/views.py,让新的index.html文件生效:
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
上面的代码会加载polls/index.html文件,并传递给它一个参数。这个参数是一个字典,包含了模板变量名和Python对象之间的映射关系。
在浏览器中通过访问/polls/,你可以看到一个列表,包含“What’s up”的问卷,以及连接到其对应详细内容页面的链接点。
如果你显示的是No polls are available.说明你前面没有添加Questions对象。没关系,我们手动添加一下就可以。
进入admin界面,选择Questions,点击右上角的Add question,如下操作。

添加完后,刷新/polls/页面。
快捷方式:render()
在实际运用中,加载模板、传递参数,返回HttpResponse对象是一整套再常用不过的操作了,为了节省力气,Django提供了一个快捷方式:render函数,一步到位!看如下代码:
polls/views.py
from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
render()函数的第一个位置参数是请求对象(就是view函数的第一个参数),第二个位置参数是模板。还可以有一个可选的第三参数,一个字典,包含需要传递给模板的数据。最后render函数返回一个经过字典数据渲染过的模板封装而成的HttpResponse对象。
四、返回404错误
现在让我们来编写返回具体问卷文本内容的视图polls/views.py:
from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
这里有个新知识点,如果请求的问卷ID不存在,那么会弹出一个Http404错误。
新建polls/detail.html文件,暂时写入下面的代码:
{{ question }}
快捷方式:get_object_or_404()
就像render函数一样,Django同样为你提供了一个偷懒的方式,替代上面的多行代码,那就是get_object_or_404()方法,参考下面的代码:
polls/views.py
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
别说我没提醒你,和render一样,也需要从Django内置的快捷方式模块中导出get_object_or_404()!
get_object_or_404()方法将一个Django模型作为第一个位置参数,后面可以跟上任意个数的关键字参数,如果对象不存在则弹出Http404错误。
同样,还有一个get_list_or_404()方法,和上面的get_object_or_404()类似,只不过是用来替代filter()函数,当查询列表为空时弹出404错误。(filter是模型API中用来过滤查询结果的函数,它的结果是一个列表集。而get则是查询一个结果的方法,和filter是一个和多个的区别!)
五、 使用模板系统
detail()视图会将上下文变量question传递给对应的polls/templates/polls/detail.html模板,修改该模板的内容,如下所示:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
在模板系统中圆点.是万能的魔法师,你可以用它访问对象的属性。在例子{{ question.question_text }}中,DJango首先会在question对象中尝试查找一个字典,如果失败,则尝试查找属性,如果再失败,则尝试作为列表的索引进行查询。
在 {% for %} 循环中的方法调用——poll.choice_set.all其实就是Python的代码poll.choice_set.all(),它将返回一组可迭代的Choice对象,并用在{% for %}标签中。
这里我们对Django模板语言有个简单的印象就好,更深入的介绍放在后面。
六、删除模板中硬编码的URLs
在polls/index.html文件中,还有一部分硬编码存在,也就是href里的“/polls/”部分:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
它对于代码修改非常不利。设想如果你在urls.py文件里修改了正则表达式,那么你所有的模板中对这个url的引用都需要修改,这是无法接受的!
我们前面给urls定义了一个name别名,可以用它来解决这个问题。具体代码如下:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
Django会在polls.urls文件中查找name='detail'的url,具体的就是下面这行:
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
举个栗子,如果你想将polls的detail视图的URL更换为polls/specifics/12/,那么你不需要在模板中重新修改url地址了,仅仅只需要在polls/urls.py文件中,将对应的正则表达式改成下面这样的就行了,所有模板中对它的引用都会自动修改成新的链接:
# 添加新的单词'specifics'
url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
七、URL names的命名空间
本教程例子中,只有一个app也就是polls,但是在现实中很显然会有5个、10个、更多的app同时存在一个项目中。Django是如何区分这些app之间的URL name呢?
答案是使用URLconf的命名空间。在polls/urls.py文件的开头部分,添加一个app_name的变量来指定该应用的命名空间:
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'),
]
现在,让我们将代码修改得更严谨一点,将polls/templates/polls/index.html中的
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
修改为:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
注意引用方法是冒号而不是圆点也不是斜杠!!!!!!!!!!!!
Part 3:视图和模板--Django从入门到精通系列教程的更多相关文章
- Part 4:表单和类视图--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- 第一章:模型层model layer -- Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 题外话: Django的教程写到这里,就进入 ...
- Django简介--Django从入门到精通系列教程
该系列教程系个人原创,并同步发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- Part 1:请求与响应--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- Part 7:自定义admin站点--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- Part 6:静态文件--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 前面我们编写了一个经过测试的投票应用,现在让 ...
- 模型和字段 -- Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- Django中不返回QuerySets的API -- Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- Part 5:Django测试--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 本节将简要介绍Django的自动化测试相关内 ...
随机推荐
- 闲来无事,把node又拾起来看看
处在国企单位,整天闲的要死要死的,民族的花朵都快枯萎了 哎呀呀,打起精神.......早睡早起,多看书...开始进入苦读状态,苦练武功,争取有朝一日报效祖国...哈哈 开玩笑的 开始看书吧 记得N ...
- VMware 设置共享目录
VMware 共享目录设置 1,选择"虚拟机"->"重新安装VMware Tools"2,挂载cdrom3,拷贝VMware-tool.tar.gz 到L ...
- java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题
一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...
- 微积分入门("SX"T版)
哎,微积分,表示暂时并没有很深入的研究……虽然高中有教,但是好像跟小西瓜学的顺序不太一样,嗯……教微积分之前不应该把极限学下来么……不管了,本文按傻X腾的理解来搞吧. 极限……大学的东西喔,我们先来认 ...
- hdu_1251统计难题(字典树Trie)
统计难题 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others)Total Submi ...
- line-height相关总结
line-height http://cindylu520.iteye.com/blog/670512 四个boxes containing boxes inline boxes 匿名inline b ...
- linux安装navicat全程记录
国庆期间自己在试着用linux(ubuntu),献上navicat安装方法,以及很多教程里没有写的一些小东西 step1: 去navicat官网下载安装包,网址:http://www.navicat. ...
- JS和H5做一个音乐播放器,附带源码
http://mp.weixin.qq.com/s/KpXT9X46AMlUVXQvpHuXGQ 效果图: 实现的功能 1.首页 2.底部播放控件 3.播放页面 4.播放列表 5.排行榜 6.音乐搜索 ...
- [拾 得] zip gzip bzip2 & tar 压缩/打包 四大金刚
坚持知识分享,该文章由Alopex编著, 转载请注明源地址: http://www.cnblogs.com/alopex/ 索引: 介绍压缩和打包 gzip bzip2 zip 的基本使用 gz ...
- 解析MYsql explain执行计划extra列输出
EXPLAIN Extra 列信息: explain Extra列输出包含了关于mysql如何解决query的额外信息,特别是出现Using filesort 和 using temporary时,应 ...