实战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/'”,这个叫正则表达式。关于正则表达式的内容,童鞋们可以参阅一些相关的书籍或文章,比如《精通正则表达式》或舍得写的正则表达式实战教程。

上面这个操作做完后,我们打开浏览器,在地址栏中输入:

可以看到我们的工作成果:

22

我们再来添加一些视图,编辑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)

看下效果:

23

数据库里的内容显示出来了。

但这种做法并不可取。因为我们把控制页面显示效果的编码放在.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包含了我们想要显示的数据。

来看下效果:

24

“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?”链接,我们就可以看到象下面这样的页面:

25

在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>

这样,以后即便应用再多,也不会起冲突了。

 

【未完待续】

本文版权归舍得学苑所有,欢迎转载,转载请注明作者和出处。谢谢!
作者:舍得
首发:舍得学苑@博客园

posted on 2014-12-06 21:07  舍得学苑  阅读(437)  评论(0编辑  收藏  举报

导航