实战Django:官方实例Part3
前面两个部分我们介绍了投票应用的框架和后台管理部分。接下来舍得要介绍这个应用面向用户的界面。
这里我们要引入一个新的概念,“视图”。在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/'”,这个叫正则表达式。关于正则表达式的内容,童鞋们可以参阅一些相关的书籍或文章,比如《精通正则表达式》或舍得写的正则表达式实战教程。
上面这个操作做完后,我们打开浏览器,在地址栏中输入:
可以看到我们的工作成果:
我们再来添加一些视图,编辑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>
这样,以后即便应用再多,也不会起冲突了。
【未完待续】