Django(三)——公众页面-视图

来源:互联网 发布:大疆没有飞行数据 编辑:程序博客网 时间:2024/05/22 18:05

Django(三)——公众页面-视图

标签(空格分隔): Django


在 Django 应用程序中,视图是一“类”具有特定功能和模板的网页。 例如,在一个博客应用程序中,可能会有以下视图:

博客首页 – 显示最新发表的博客。
博客详细页面 – 一篇博客的独立页面。
基于年份的归档页 – 显示给定年份中发表博客的所有月份。
基于月份的归档页 – 显示给定月份中发表博客的所有日期。
基于日期的归档页 – 显示给定日期中发表的所有的博客。
评论功能 – 为一篇给定博客发表评论。

在Django中视图是个简单的python函数(或是方法,在基于类的情况下)
Django通过检查请求的URL来匹配视图,其中起作用的是URLconfs配置文件(还是一个.py文件),它定义一种URL模式(正则式)以映射到视图文件上。

新建一个视图

打开polls/views.py,编辑:

from django.http import HttpResponsedef index(request):    return HttpResponse("Hello, world. You're at the polls index.")

为了调用这个视图,我们将它映射到一个URL上,怎么映射呢?相同目录下新建一个urls.py,编辑:

from django.conf.urls import patterns, url #调用url包中的模式匹配与url功能from polls import views #调入建好的视图urlpatterns = patterns('', #url模式定义    url(r'^$', views.index, name='index'))

然后将映射好的url再次映射到项目文件的URLconf配置中(即mysite/urls.py):

from django.conf.urls import patterns, include, urlfrom django.contrib import adminurlpatterns = patterns('',    url(r'^polls/', include('polls.urls')), #这一行便是映射,使用了include()方法    url(r'^admin/', include(admin.site.urls)),)

url() 函数有四个参数,两个必须的: regexview, 两个可选的: kwargs, 以及 name。 接下来,来探讨下这些参数的意义。

regex是 regular expression 的简写,这是字符串中的模式匹配的一种语法, 在 Django 中就是是 url 匹配模式。 Django 将请求的 URL 从上至下依次匹配列表中的正则表达式,直到匹配到一个为止。

View当 Django 匹配了一个正则表达式就会调用指定的视图功能,包含一个 HttpRequest 实例作为第一个参数和正则表达式 “捕获” 的一些值的作为其他参数。 如果使用简单的正则捕获,将按顺序位置传参数;如果按命名的正则捕获,将按关键字传参数值。

kwarfs任意关键字参数可传一个字典至目标视图。任意关键字参数可传一个字典至目标视图。

name命名你的 URL,让你在Django的其他地方明确地引用它,特别是在模板中。

编辑更多视图

同样在应用的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)

在应用urls.py中映射:

from django.conf.urls import patterns, urlfrom polls import viewsurlpatterns = 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'),)

这时可以谈谈在mysite/urls.py中使用include()的好处,在语句url(r'^$polls/', include('polls.urls'))中,当正则匹配到polls时即将url后面的部分交给include()里的URLconf配置文件进行下一步配置,是应用的视图可以即插即用,方便修改。

比如,当用户访问www.mysite.com/polls/1/时,Django首先在mysite/urls.py寻找对于r'^polls/'字段的匹配,然后字段/polls/1/后面的1/传入polls/urls.py中进行下一步处理。

于是,url(r'^$polls/', include('polls.urls'))在url中匹配的结果被include()中的URLconf带进去处理了,此时poll/urls.py中的语句
url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),捕获到数字1,正则式 r'^(?P<question_id>\d+)/$'匹配到的内容被传入View参数中(即views.detail),此时传入参数的形式为:
detail(request=<HttpRequest object>, question_id='1')
其中,question_id来自于?P<question_id>所定义的标识的匹配内容名称,\d+用来匹配数字,即匹配的内容。

在视图中添加实际的功能

每个视图都只做两件事:返回HttpResponse对象,包含了所请求页面的内容;抛出一个异常,比如404。
其他的就靠自己去添加了,比如读取数据库、使用模板、调用第三方库什么的
比如在视图polls/views.py.index()上编写:

from django.http import HttpResponsefrom polls.models import Questiondef 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)

可以看出返回的output内容为5个Question,按发表日期排序。
在此,视图页面的设计是直接由views.py设定的,这种被称为硬编码的方式对程序员和设计师都很不友好,于是我们可以新建一个模板,让页面设计脱离开来。

创建视图模板

在应用文件夹内新建一个templates目录,在里面新建一个应用名目录(polls),在polls目录中新建一个index.html文件

此处涉及为何要新建一个中间层目录polls,这是因为在应用不止一个时,Django会区分不了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.py中写的是一样的,该怎么应用这个模板呢?回到views.py:

from django.http import HttpResponsefrom django.template import Context, loader# 上面这个就是使用模板所导入的from polls.models import Polldef index(request):    latest_poll_list = Poll.objects.order_by('-pub_date')[:5]    template = loader.get_template('polls/index.html')# 寻找polls/index.html模板    context = Context({        'latest_poll_list': latest_poll_list,    }) # 导入的字典参数,index模板处理的不是context对象而是其传入的对象    return HttpResponse(template.render(context))

在加载这个模板时,代码将context提交给模板进行渲染处理
为了简化代码,Django提供了一种快捷方式,render()来完成加载模板,传递参数,返回HttpResponse对象的功能,这样便不再要求导入HttpResponse、Context、loder,而只需要

from django.shortcuts import render# Django提供的快捷方式包from polls.models import Questiondef 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) # request即HttpResponse对象,其次是模板及模板需要的字典参数 

从404开始,抛出一个异常

当申请一个数据库中并不存在的页面时,按照惯例我们需要抛出一个404异常
在这个poll应用中的该如何实现?编辑views.py中的detail视图:

from django.http import Http404from django.shortcuts import renderfrom polls.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})

这里新添加了一个异常捕获,当question_id并不存在在Question中时便返回一个Http404对象。同样,Django提供了404的快捷方式:

from django.shortcuts import get_object_or_404, renderfrom 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})

可以看到get_object_or_404(Question, pk=question_id)完成了一次异常捕获已及正常的赋值功能。
这里还出现了detail.html的模板,下面来对应用下的其他视图页面进行设置模板

使用模板系统

刚才已经创建过一次应用模板,现在再次创建一个层次更高的应用模板对应其他视图,要知道一个应用的逻辑结构通常是多叉树形的,比如poll应用进入具体的某一个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>

以此为例谈谈Django对模板语句{{ question.question_text }}的识别,这是一种以dot(.)为句式结构区分对象以及对象属性的语法,模板系统首先对question对象进行字典查找(在.之前),没有则进行对象后的question_text进行查找(这里查找成功了),最后还没找到则会进行list_index查找(这个有待研究)
此外{% for %}语句中的question.choice_set.all即等于python语句question.chocie_set.all()

移除模板中的硬编码URL

在模板index.html中对另一个模板的URL的链接是这样的:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

这种硬编码在要修改URL时工程量很大(/polls/作为一个与代码直接相关的字段出现在模板中很不好),当然这是可以修改的,还记得在polls/urls.py中有关detail的url()属性name么?这里可以利用这个命名参数将/polls/取代,即将该行修改为:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li> # 使用{% url %}来选择urls.py中的url命名参数

可能对上一段话还不是很理解,那么来做个实验,在polls/urls.py中,将
url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),该为
url(r'^specifics/(?P<question_id>\d+)/$', views.detail, name='detail'),如果还是之前的硬编码,index.html中的链接就会失效(因为detail的URL地址已经多了specifics/但链接里还是polls//)

进一步探讨命名空间

前面已经说到为了防止Django混淆同一项目不同应用间同样的模板而将templates模板目录分开存放且加上应用名(polls)作为命名空间,而使用了刚才的{% url %}
之后要怎么链接到正确的模板呢?答案就是命名空间了,首先在项目根URLconf配置文件mysite/urls.py中对应用的url进行命名空间声明,即改为:

url(r'^polls/', include('polls.urls', namespace="polls")),

然后,将模板中的链接中修改为具体某一个应用的视图,即:

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

这样就保证了Django不会混淆不同应用下的同名视图模板了。

总结

现在可以进行应用视图的定义:

在应用的views.py中定义视图函数,ex:def index(request):
将其映射到一个url上,在urls.py中 :url(r'^$',views.index,name='')以及关于url()的四个参数
在项目中映射应用的url,在mysite/urls.py中添加: url(r'^polls/',include('polls.urls')),以及include()的功能
添加更多(含参)视图

使用模板以及实现模板的实际功能

在模板中使用应用的models
以及参数如何传入模板中、render()快捷方式的实现
抛出一个404异常以及其快捷方式

有关URLconf的思考

移除模板中的硬编码以及命名空间的使用

0 0