前面两个部分我们介绍了投票应用的框架和后台管理部分。接下来舍得要介绍这个应用面向用户的界面。
这里我们要引入一个新的概念,“视图”。在Django中,视图是一根连接模型和模板的纽带,它决定哪些数据要呈现给用户。
来看一下Django的这几个核心概念:
- 模型:负责和数据库打交道,把数据传入、传出给数据库;
- 模板:负责最终显示给用户的页面的显示方式;
- 视图:将模型和模板连接在一起,它决定了哪些数据要显示给用户;
- 链接:Django中有一项url配置,它充当着整个程序的入口,用户在地址栏中输入地址时,由链接模块去决定将这个地址分发到哪里;
带着这些概念,我们来写下本例中的第一个视图:
14.编写视图
编辑polls/views.py 文件,添加下面的内容:
polls/views.py:
from django.http import HttpResponse
def index(request):
return HttpResponse("嗨,哥们,你现在正在访问的是投票应用的首页.")
这可能是Django中最简单的视图了。要想让视图的内容最终呈现给用户,我们还需要将这个视图关联(术语叫映射)到URL文件中。
现在我们需要在polls文件夹下创建一个叫url.py的文件,创建完后,你的应用文件夹应该是下面这个样子:
polls/
__init__.py
admin.py
models.py
tests.py
urls.py
views.py
编辑polls/urls.py 文件,加入下面这些代码:
polls/urls.py :
from django.conf.urls import patterns, url
from polls import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
)
此时,从应用到用户那条路还没打通,我们还需要在站点的urls.py中把链接指向刚编辑完的urls.py。
编辑mysite/urls.py 文件,让它变成下面这样:
mysite/urls.py :
from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = patterns('',
url(r'^polls/', include('polls.urls')),
url(r'^admin/', include(admin.site.urls)),
)
注意观察第四行,它把我们的polls/urls.py关联到了mysite/urls.py 中。
url()中的第一个参数,如”r'^polls/'”,这个叫正则表达式。关于正则表达式的内容,童鞋们可以参阅一些相关的书籍或文章,比如《精通正则表达式》或舍得写的正则表达式实战教程。
上面这个操作做完后,我们打开浏览器,在地址栏中输入:
http://127.0.0.1:8000/polls/
可以看到我们的工作成果:
我们再来添加一些视图,编辑polls/views.py 文件,添加下面的内容:
polls/views.py:
def detail(request, question_id):
return HttpResponse("你正在查看的是问题%s." % question_id)
def results(request, question_id):
response = "你正在查看的是问题%s的投票结果."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("你正在为问题%s投票." % question_id)
现在我们只需要编辑polls/urls.py 文件就可以让视图生效了:
polls/urls.py :
from django.conf.urls import patterns, url
from polls import views
urlpatterns = patterns('',
# 如: /polls/
url(r'^$', views.index, name='index'),
# 如: /polls/5/
url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),
# 如: /polls/5/results/
url(r'^(?P<question_id>\d+)/results/$', views.results, name='results'),
# 如: /polls/5/vote/
url(r'^(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
)
转到浏览器界面,分别用下面这些链接去访问一下:
http://127.0.0.1:8000/polls/1/
http://127.0.0.1:8000/polls/1/result
http://127.0.0.1:8000/polls/1/vote
我们来分析一下Django的处理流程。在本例中,当用户访问到http://127.0.0.1:8000/polls/1/results这样的链接时:
- Django会先在mysite/urls.py 中找“^poll/”;
- 找到后,Django会去掉“poll/”,把剩下的“1/results”发到polls/urls.py 中继续处理;
- 在polls/urls.py 发现能匹配到“r'^(?P<question_id>\d+)/results/$'”,最终显示polls/views.py中的results视图;
15.进阶版视图
前面的视图仅仅是让大家熟悉Django的基本处理流程。现在,我们来进入实战状态,编写真正有用的视图。
编辑polls/views.py 文件,让它变成下面这样:
polls/views.py:
from django.http import HttpResponse
from polls.models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ', '.join([p.question_text for p in latest_question_list])
return HttpResponse(output)
# 下面的那些视图暂时留着别删 (detail, results, vote)
看下效果:
数据库里的内容显示出来了。
但这种做法并不可取。因为我们把控制页面显示效果的编码放在.py文件中,这样的方式我们称之为”硬编码“,它意味着以后每一次要改动页面效果,都需要修改这个.py文件,显然极为不便。
正确的做法是,把页面显示相关的部分交给模板系统来处理。
首先,请在polls文件夹下建立templates文件夹,Django会自动搜索到它。
在templates文件夹中建立一个文件夹,叫polls,然后在这个polls文件夹下新建一个index.html文件,这是我们的投票应用的首页模板。这个模板的路径应该是:
polls/templates/polls/index.html
把下面这些代码添加到index.html中(Html文件用文本编辑器来完成就可以了,舍得推荐使用emeditor):
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 文件,改成这样:
polls/views.py:
from django.http import HttpResponse
from django.template import RequestContext, loader
from polls.models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = RequestContext(request, {
'latest_question_list': latest_question_list,
})
return HttpResponse(template.render(context))
在上面这段代码中,我们先载入“polls/index.html”这个模板,传递给它一个叫context的对象,这个context包含了我们想要显示的数据。
来看下效果:
“What's Up?”变成了链接的形式,链接指向了投票的内容页。
上面这样的写法,在Django中有更便捷的方式,我们来修改polls/views.py 文件:
polls/views.py :
from django.shortcuts import render
from polls.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()函数使用request作为第一个参数,模板名作为第二个参数,把context这个字典类型数据作为第三个参数。它会用数据用模板渲染好,呈现到用户面前。
16.处理404错误
现在,让我们来着手设计投票内容页。
编辑polls/views.py 文件:
polls/views.py :
from django.http import Http404
from django.shortcuts import render
from polls.models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404
return render(request, 'polls/detail.html', {'question': question})
这里引入了一个新的概念,当用户访问的投票项不存在时,视图会抛出一个Http404异常来通知用户。
给上面这个视图配上对应的模板,建立detail.html文件 ,并添加以下内容:
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>
刚才那个视图,Django里有更为便捷的写法,编辑polls/views.py 文件,把它改成这样:
polls/views.py :
from django.shortcuts import get_object_or_404, render
from polls.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})
现在,点击刚才浏览器中的那个“What's Up?”链接,我们就可以看到象下面这样的页面:
在Django的模板系统中,我们要注意的是两类符号,一类是双大括号,如:`question`.`question_text `;另一类是{% 的形式,如{% for choice in question.choice_set.all %}。前者仅仅是一个值,后者则相当于一段程序。
在本例中,{% for %} 代表了一个for循环,question.choice_set.all包含了这个投票的所有选项,用一个for循环就可以把它全部显示出来。
17.移除模板中的硬编码
在我们刚才的模板中,也存在着我们前面说过的硬编码问题。
像下面这样的代码:
<li><a href="/polls/` question`.`id `/">` question`.`question_text `</a></li>
这里的”/polls/` question`.`id `/“采用常量的方式写入链接,一旦以后有变动,要修改链接,可能会产生大量的额外工作量。
好在我们可以解决这个问题。上面的这段代码我们可以写为:
<li><a href="{% url 'detail' question.id %}">` question`.`question_text `</a></li>
当此模板被呈现的时候,Django会自动到你定义的urls.py文件中去寻找”detail”这个名称,它会在polls/urls.py中找到:
...
# 这里的'name'值就是{% url %} 模板标签调用的地方。
url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),
...
这样,你要修改链接,比如把它改0“成polls/specifics/12/”,不用去逐个修改模板,只要改动polls/urls.py就可以了,如:
...
# 链接中加入'specifics'
url(r'^specifics/(?P<question_id>\d+)/$', views.detail, name='detail'),
...
18.创建URL命名空间
我们这个例子只有一个应用,而在真实的项目中,往往五个、十个、二十个甚至更多的应用。在这种时候,url名称会不会被搞混掉?举个例子,我们这个polls应用有个叫detail的视图,某一天,在同一项目下增加了一个blog的应用,它也有一个detail视图,怎么来区分呢?
这就是命名空间存在的意义。
编辑mysite/urls.py文件,增加命名空间的属性:
mysite/urls.py:
from django.conf.urls import patterns, include, url
from django.contrib import admin
urlpatterns = patterns('',
url(r'^polls/', include('polls.urls', namespace="polls")),
url(r'^admin/', include(admin.site.urls)),
)
修改模板文件polls/index.html,把它从:
polls/templates/polls/index.html :
<li><a href="{% url 'detail' question.id %}">` question`.`question_text `</a></li>
改为:
polls/templates/polls/index.html :
<li><a href="{% url 'polls:detail' question.id %}">` question`.`question_text `</a></li>
这样,以后即便应用再多,也不会起冲突了。
【未完待续】
本文版权归舍得学苑所有,欢迎转载,转载请注明作者和出处。谢谢!
作者:舍得
首发:舍得学苑@博客园