编写第一个Django程序(3)

接着我们的第二部分,我们继续.下面我们将要把注意力转移到创建视图部分.

原理:

在Django的程序中,一个视图就同属一类的网页.它提供特定的功能,拥有特定的模板.例如,一个博客程序你可能有以下一个视图:

  1. 首页,显示最近添加的文章.
  2. 文章的详细页面
  3. 基于年份的文章归档
  4. 基于月份的文章归档
  5. 基于天的文章归档
  6. 添加评论的动作-处理文章的评论

在我们的民意测试投票的程序中,我们需要有以下一些视图:

  1. index页.显示最近的民意测试投票项目
  2. 民意测试投票项目的详细页面,一个投票的所有问题,没有答案,只给吃一个投票的表单
  3. 投票的结果页
  4. 投票的动作,给特定的投票添加投票

在Django里,每一个视图都由一个简单Python函数来处理

设计你的URLs

开始编写视图前的第一个步应该是设计你的URL结构,你可以通过创建Python 的模块来完成.这个模块就是URLconf.URLconf就是Django用来完成具体请求和Python代码对应的功能.

当一个用户请求有Django构建的网站时,系统会首先去根目录下的settings.py文件里查看ROOT_URLCONF配置.它的值就是一个string,用Python的点语法写的.得到这个值后Django就会去指定的模块文件里查找对应的Python函数.

当在教程的第一部分中建立mysite这个网站的时候,就已经在根目录下创建了一个urls.py这个文件,它也同时被指向到了settings.py文件中的ROOT_URLCONF.

下面我们修改mysite/urls.py文件:

from django.conf.urls import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', url(r'^polls/$', 'polls.views.index'), url(r'^polls/(?P\d+)/$', 'polls.views.detail'), url(r'^polls/(?P\d+)/results/$', 'polls.views.results'), url(r'^polls/(?P\d+)/vote/$', 'polls.views.vote'), url(r'^admin/', include(admin.site.urls)), )

这段代码需要我们来重新阅读一遍,例如当一个请求”/polls/23/”来访问网站时,Django就会加载urls.py,因为在settings.py文件里的ROOT_URLCONF就指向它,然后就会寻找到urlpatterns变量,它是一个序列,然后就会挨个的解析它,当找到与请求对应的url(r’^polls/(?P\d+)/$’)时,就会映射到polls目录下的views.py文件里的detail()函数.相当于最后一个http请求直接请求了这个函数.

detail(request=, poll_id=’23’)

这个poll_id=’23’部分来自于(?P\d+).使用括号来包围这个样式.?P定义了名称用来鉴别对应的模型. \d+是一个正则表达式用来匹配参数.(例如数字).

因为url是用正则表达式写的,所有就没有任何显示对于你来说.当然也没有必要跟着文件的后缀名.例如.php.当然你想写也行:

(r'^polls/latest.php$', 'polls.views.index')

但是还是别这样做,很傻.

注意的是这些正则不会搜索GET和POST的参数,或者域名.例如这样一个请求:http://www.example.com/myapp/ . URLconf就会对应到myapp/ .http://www.example.com/myapp/?page=3这样的请求还是会对应到myapp/ .其实就是说它的模式完全是REST风格的,而不是传统的.querystring是不起作用的!

如果需要关于正则表达式的知识请阅读Wikipedia's entry.还有关于re模块的文档最后,性能的考虑,这些正则会在URLconf模块第一个加载的时候被编译,他们相当的快!

开始写你的第一个视图:

好的,到目前为止我们还没有写过一个视图.我们只是有个URLconf文件.我们来确认Django已经加载了它.启动server:

python manage.py runserver

我们输入这个地址: “http://localhost:8000/polls/”,接着你会看见这个错误:

ViewDoesNotExist at /polls/ Tried index in module polls.views. Error was: 'module' object has no attribute 'index'

出这个错因为是你没有在polls/views.py里写index()函数.你还可以试试 “/polls/23/”, “/polls/23/results/” and “/polls/23/vote/”. 这些,同样Django尝试去寻找对于的函数,但是失败了,那是因为我们还没有给views.py中添加任何代码. 是的,是时候来在views.py里添加点代码了:

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

输入/polls/这可能是你见过的最简单的页面了.让我们来多创建几个视图吧,下面的可能有点不同,因为这些请求带了点参数.(都是从哪些URLconf里的正则表达式来的)

def detail(request, pollid): return HttpResponse("You're looking at poll %s." % pollid) def results(request, pollid): return HttpResponse("You're looking at the results of poll %s." % pollid) def vote(request, pollid): return HttpResponse("You're voting on poll %s." % pollid)

下面你在地址里输入,它将会调用detail()函数,会在页面上显示你输入的ID.试试“/polls/34/results/”* “/polls/34/vote/”* .这些会显示投票页面.

接下来让我们的views做点实事吧。

每一个视图基本上会做1到2件事情。返回http请求对应的http响应。或者就是返回一个异常,比如说404.

你的视图能够去数据库读取数据,然后可以使用Django的模板系统,或者Python的第三方模板。设置都可以不用,它能够生成PDF文件,输入XML文件,创建一个zip压缩包等等。简单说就是你能够做任何事情,使用你想要的任何Python库。

Django想要的就是一个Http响应或者就是一个异常。

我们将用Django自己的数据库API。因为它很方便使用,我们在教程的第一个部分中提到过。下面的代码是取了最近的5条民意测试。每条由逗号分隔,按照发布时间排序:

from polls.models import Poll from django.http import HttpResponse def index(request): latestpolllist = Poll.objects.all().orderby('-pubdate')[:5] output = ', '.join([p.question for p in latestpolllist]) return HttpResponse(output)

但是问题就来了,给页面返回的内容是我们硬编码的,如果我们想要改变一下页面的外观,还要到这个文件里重新编辑。这个时候我们就需要使用模板文件了。

from django.template import Context, loader from polls.models import Poll from django.http import HttpResponse def index(request): latestpolllist = Poll.objects.all().orderby('-pubdate')[:5] t = loader.gettemplate('polls/index.html') c = Context({ 'latestpolllist': latestpoll_list, }) return HttpResponse(t.render(c))

上面的代码中提到了一个名叫“polls/index.html”模板文件。然后把内容值给了这个页面。然后你你就可以去浏览器看看了。不过可惜的是会报错的:

TemplateDoesNotExist at /polls/ polls/index.html

为什么呢?原来我们还没有配置模板呢。怎么做呢?首先创建一个目录。位置随意,只要Django可以访问到。安全起见,别让他们给公共了。然后,编辑你的工程mysite目录下的settings.py文件。找到TEMPLATE_DIRS,然后把它指向到你刚才创建的那个模板地址.这就是告诉Django到哪里去找模板.完成之后在模板目录下新建一个polls目录.然后创建一个index.html文件。这个时候可以知道它是怎么工作的了。其实就是

loader.gettemplate('polls/index.html')==》"[templatedirectory]/polls/index.html"

然后在index.html写这些代码:

{% if latestpolllist %}

{% else %}

No polls are available.

{% endif %}

到这里就重新去请求刚才的那个路径吧:http://localhost:8000/polls/ 就将会看见一个列表显示的民意测试结果。每一个都链接到了详细页面。

简写:rendertoresponse()

请求数据,然后加载模板,最后把数据返回给模板,模板做展示。这可能是最常见的想法了。同时Django还提供了一个这个过程的一个简写。那就是rendertoresponse()方法。看代码:

from django.shortcuts import rendertoresponse from polls.models import Poll def index(request): latestpolllist = Poll.objects.all().orderby('-pubdate')[:5] return rendertoresponse('polls/index.html', {'latestpolllist': latestpolllist})

这里要注意一点就是一旦我们这样写了之后就不需要再引用loader, Context 和 HttpResponse.这个rendertoresponse()方法的第一个参数就是模板的地址,第二个可选的参数就是要返回的结果字典。

404页面

现在,我们来编写投票的详细页面,这个页面会显示民意调查的几个问题:

from django.http import Http404 # ... def detail(request, pollid): try: p = Poll.objects.get(pk=pollid) except Poll.DoesNotExist: raise Http404 return rendertoresponse('polls/detail.html', {'poll': p})

这个我们要解决的就是要查询的ID不存在的情况,会返回一个404的页面。我们写在polls目录下新建一个detail.html页面。待会我们再说模板怎么写,你先简单的写一个{{ poll }} 去浏览器试试看吧。

对于刚才的那个try catch块同样有一个简写就是getobjector_404()这样一个函数。可以这样写:

from django.shortcuts import rendertoresponse, getobjector404 # ... def detail(request, pollid): p = getobjector404(Poll, pk=pollid) return rendertoresponse('polls/detail.html', {'poll': p})

原理:
我们为什么要使用 getobjector_404() 帮助方法来替代自动捕捉异常的ObjectDoesNotExist的方式呢?或者使用对象api的Http404来替代ObjectDoesNotExist呢?
因为我们又是在一个页面视图上会返回很多的对象。为了能够解耦合,我们就这样设计了。

同样还有一个getlistor404()的方法。和get——objector_404()是一样的工作原理。

使用模板系统
回到刚才的详细页面的视图文件,下面是我们修改的detail.html文件:

{{ poll.question }}

    {% for choice in poll.choice_set.all %}
  • {{ choice.choice }}
  • {% endfor %}

在模板系统里我们使用点操作符来访问对象的属性,在例子中,我们能够通过 {{ poll.question }}访问question属性,首先Django会去查找对象poll的字典,如果失败就会去找属性。在这个例子中,方法的调用是在{% %}代码块中。有一个for循环。 poll.choiceset.all代表的就是Python代码中的 poll.choiceset.all()。它将返回一个可以遍历的选项集合。

简化我们的URLconf配置
在我们折腾视图和模板系统的时候,你会标记URLconf文件,你可能注意到了有那么一点的重复内容存在:

urlpatterns = patterns('', url(r'^polls/$', 'polls.views.index'), url(r'^polls/(?P\d+)/$', 'polls.views.detail'), url(r'^polls/(?P\d+)/results/$', 'polls.views.results'), url(r'^polls/(?P\d+)/vote/$', 'polls.views.vote'), )

在什么的路径中,每次请求都会包含polls.views路径。为了解决这个常见的问题,URLconf框架提供了一个共通的前缀,你可以把它提出来让在patterns()里的第一个参数位置:

urlpatterns = patterns('', url(r'^polls/$', 'polls.views.index'), url(r'^polls/(?P\d+)/$', 'polls.views.detail'), url(r'^polls/(?P\d+)/results/$', 'polls.views.results'), url(r'^polls/(?P\d+)/vote/$', 'polls.views.vote'), )

也行你不想给应用的每一个url都添加上前缀,这个时候就可以把他们分开写。然后再合在一起:

from django.conf.urls.defaults import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns('polls.views', url(r'^polls/$', 'index'), url(r'^polls/(?P\d+)/$', 'detail'), url(r'^polls/(?P\d+)/results/$', 'results'), url(r'^polls/(?P\d+)/vote/$', 'vote'), ) urlpatterns += patterns('', url(r'^admin/', include(admin.site.urls)), )

分解我们的url配置
到这里我们就需要花点时间来分解polls的url我们的url配置了。我们要把polls的工程的url配置从系统的urls.py文件中分离出来。Django的设计初衷就是能够实现持续的可拔插。一个应用应该很容易就迁移到别的Django工程下。

我们把mysite/urls.py拷贝一份到polls目录下。然后编辑它:(这里我们为这个目录下的所有url都加了前缀,所以我们就去掉之前的在每个url中的polls/views)

from django.conf.urls.defaults import patterns, include, url urlpatterns = patterns('polls.views', url(r'^$', 'index'), url(r'^(?P\d+)/$', 'detail'), url(r'^(?P\d+)/results/$', 'results'), url(r'^(?P\d+)/vote/$', 'vote'), )

这里只是polls应用的url设置。
下面我们来修改工程的url配置:(我们通过include()方法来加载polls目录下的url配置文件。)

This also imports the include function from django.conf.urls.defaults import * from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', url(r'^polls/', include('polls.urls')), url(r'^admin/', include(admin.site.urls)), )

期待下一部分(4)~