Django笔记 3

接下来我们需要设计视图和页面了,一共要有四个视图:index,detail,vote,results,相应的也会有四个页面。

URL映射

首先需要解决的就是url映射,django在使用URL时首先找到ROOT_URLCONF这个设置(在settings.py里面),然后加载这个module,寻找里面的一个叫做urlpatterns的变量。它是一个元组,其中每一项的格式是“(regular expression,callback function[,optional dictionary])”。找到这个变量之后就从头到尾地进行匹配,匹配成功之后就调用回调函数,并将HttpRequest作为第一个参数。

在urls.py里添加关于投票的url映射信息:

urlpatterns = patterns(’’,
url(r’^polls/$’, ’polls.views.index’),
url(r’^polls/(?P<poll_id>\d+)/$’, ’polls.views.detail’),
url(r’^polls/(?P<poll_id>\d+)/results/$’, ’polls.views.results’),
url(r’^polls/(?P<poll_id>\d+)/vote/$’, ’polls.views.vote’),
url(r’^admin/’, include(admin.site.urls)),
)

实现视图和模版

这里的所有view都没有实现,那么,下面就简单实现一下了:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You’re at the poll index.")
def detail(request, poll_id):
    return HttpResponse("You’re looking at poll %s." % poll_id)
def vote(request, poll_id):
    return HttpResponse("You’re voting on poll %s." % poll_id)
def results(request, poll_id):
    return HttpResponse("You’re looking at the results of poll %s." % poll_id)

这仅仅是个最简单的示例,以后还要一一实现。要注意,每一个view要么返回一个HttpResponse,要么抛出Http404异常。

index视图是用来显示投票列表的,为了不显示太多,我们把它修改为只显示最近的5个:

from django.http import HttpResponse
from polls.models import Poll

def index(request):
    latest_poll_list=Poll.objects.all().order_by('-pub_date')[:5]
    t=loader.get_template("polls/index.html")
    c=Context({'latest_poll_list':latest_poll_list,})
    return HttpResponse(t.render(c))

这里用到的就是django里面的模版渲染:载入一个html模版,然后将context传入进去。context是一个字典,字典的项就是模版中要用到的变量。

我们之前设置过所有模版的根文件夹,即TEMPLATE_DIRS这个变量,只需要在下面建立polls/index.html就行了:

{% if latest_poll_list %}
<ul>
{% for poll in latest_poll_list %}
<li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}

如果现在运行服务器,就可以看到http://localhost:8000/polls/这个页面了,显示的内容就是所有的投票的问题。

两个捷径

第一个是django提供的模版渲染捷径:render_to_response()。这个方法有两个参数:模版页面和词典,因此上个例子可以改为:return render_to_response("polls/index.html",{'latest_poll_list':latest_poll_list})

在index页面的每一项会超连接到detail页面,首先会根据投票的id从数据库中找到相应的对象,当然,如果找不到就会抛出ObjectDoesNotExist异常,因此,detail视图应该是这样的:

def detail(request, poll_id):
    try:
        p=Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404
    return render_to_response('polls/detail.html',{'poll':p})

这就要引出我们说的第二个捷径了:get_object_or_404。它将一个model作为第一个参数,把get object时用到的一系列关键词作为第二个参数。因此上面的try except可以合并为一句:p = get_object_or_404(Poll, pk=poll_id)

要多说的是,如果真的出现了404错误,django就会调用URLConf中的handler404这个视图,你需要定义它,但如果没有,就默认调用django.views.defaults.page_not_found()这个视图。此外,还要自己实现一个404.html,因为本来没有这个页面,再加上DEBUG被设为False的话,就会产生HTTP500,500的意思是server error。当然,你也可以像404错误一样设定handler500.

改进URL映射

patterns的第一个参数的意思是下面所有view的共同前缀,因为关于投票的四个视图都以polls.views开头,因此可以改为:

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

但是admin和它们又没有共同前缀,因此可以写成这样:

urlpatterns = patterns(’polls.views’,
url(r’^polls/$’, ’index’),
url(r’^polls/(?P<poll_id>\d+)/$’, ’detail’),
url(r’^polls/(?P<poll_id>\d+)/results/$’, ’results’),
url(r’^polls/(?P<poll_id>\d+)/vote/$’, ’vote’),
)
urlpatterns += patterns(’’,
url(r’^admin/’, include(admin.site.urls)),
)

前面说过,每一个application都是可插拔的,它独立于整个工程存在,但是这里的urls.py是在project文件夹下面的,也就是说,关于投票这个应用的url信息出现了工程的文件夹下。所以,为了实现松耦合,因此我们需要做一些改动:将mysite文件夹下的urls.py拷贝到polls文件夹下面,并将mysite/urls.py改为:

from django.conf.urls import patterns, include, url
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns(’’,
url(r’^polls/’, include(’polls.urls’)),
url(r’^admin/’, include(admin.site.urls)),
)

include语句调用了另一个URLConf,这时,django匹配过程变为首先找到polls/,如果匹配,将剩下的部分传递给引用的URLConf中。因此,polls/url.py应为:

from django.conf.urls import patterns, url

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

之前有一个模版文件index.html里用到了引用<a href="/polls/{{ poll.id }}/">{{ poll.question }}</a> ,现在应改为:<a href="{% url polls.views.detail poll.id %}">{{poll.question}}</a>。django的url语法是这样的,{%url path-to-view args1 args2 %}.

更改detail模版

我们之前的polls/detail.html模版仅仅是显示出了问卷的问题,让我们把这个模版改一下,里面加上一个form表单:

<!-- 显示出这个投票的题目 -->
<h1>{{poll.question}}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<!-- 表单提交到vote视图 -->
<form action=" {% url polls.views.vote poll_id=poll.id %}" method="post">
<!-- 为了防止跨域攻击,使用下面的标签 -->
{% csrf_token %}
{% for choice in poll.choice_set.all %}
    <!-- value值为选项的choice_id,因此,当表单提交的时候也就提交了choice_id,即"choice=choice_id" -->
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text}} </label> <br/>
{% endfor %}
<input type="submit" value="vote"/>
</form>

由于我们使用了csrf_token标签,因此就不能用context了,而要用RequestContext:

return render_to_response(’polls/detail.html’, {’poll’: p},context_instance=RequestContext
(request))

表单提交后到了vote视图:

def vote(request,poll_id):
    #找到传进来的poll
    p=get_object_or_404(Poll,pk=poll_id)
    try:
    #之前表单传入的参数是choice,值是choice_id,这里使用request.POST方法将它的值提取出来
        selected_choice=p.choice_set.get(pk=request.POST['choice'])
    #提取错误时,显示错误信息
    except(KeyError,Choice.DoesNotExist):
        return render_to_response('polls/detail.html',{
        'poll':p,
        'error_message':"You didn't select a choice.",
        },context_instance=RequestContext(request))
    #没有错误就给选中的那一项投一票,然后保存,将来写入到数据库中
    else:
        selected_choice.votes+=1
        selected_choice.save()
      #页面跳转到results视图
        return HttpResponseRedirect(reverse('polls.views.results',args=(p.id,)))

reverse就是指定跳转的视图,定义是reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)。results视图用来显示结果:

def results(request,poll_id):
    p=get_objects_or_404(Poll,pk=poll_id)
    return render_to_response('polls/results.html',{'poll':p})

results.html的内容:

<h1>{{ poll.question }}</h1>

<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes}} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url polls.views.vote poll_id=poll.id %}">Vote again?</a>

最后不说了,上图:

 

 

posted @ 2012-11-15 21:15  cubika  阅读(298)  评论(0编辑  收藏  举报