zoukankan      html  css  js  c++  java
  • Hello World 之Django(下)

    Django采用MVC的结构。其中的View通常来实现一定功能,并且有一个模板。在Django中,每一个view用一个函数来表示。例如在我们接下来要实现的例子中,将有4个view:index页面,显示最新的几个Poll,Poll detail页面,结果页面,和投票页面。

    在Django中,编写view的第一步是配置URL结构,通过URLconf模块来实现页面到python代码的映射。当接到一个HTTP请求的时候,django首先检查ROOT_URLCONF 的配置,这是一个模块的名字,django会在这个模块中寻找urlpatterns这个变量,urlpatterns由一组tuple组成,每个tuple的内容是:

    (regular expression, Python callback function [, optional dictionary])

    含义是:符合第一个正则式的url将会调用第二参数中的python函数,第一个参数是一个HttpRequest对象,其他参数在最后的dictionary中。在我们的例子中,django在settings.py中自动生成了如下配置:

    ROOT_URLCONF = 'mysite.urls'

    在mystie目录下有个urls.py,其中有urlpatterns变量:

    urlpatterns = patterns('',
        # Example:
        # (r'^mysite/', include('mysite.foo.urls')),
    
        # Uncomment the admin/doc line below to enable admin documentation:
        # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    
        # Uncomment the next line to enable the admin:
        (r'^admin/', include(admin.site.urls)),
    )

    按照提示,改成我们需要的:

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

    django会依次检查每个正则表达式,然后调用相应的方法。例如,如果有个请求/polls/23/,第三条规则导致的函数调用如下:

    detail(request=<HttpRequest object>, poll_id='23')

    注意,这些正则表达式忽略GET参数,也就是url中?之后的内容。

    接下来开始实现view,在view.py中定义如下方法:

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

    启动服务器,在浏览器中访问http://127.0.0.1:8000/polls/ 就可以看到上面那句Hello world。说明配置的url映射已经起效。为了生成一个完整的html,最好采用模板的形式,目前还没有模板,在polls目录中新建一个index.html,内容如下:

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

    模板文件的语法和其他类型的脚本语言很相似。为了让dajango找到模板文件,需要一点配置,在settings.py中有个TEMPLATE_DIR变量,将模板文件的位置指定下。下面看如何在python中用数据填充这个模板。在views.py中写入如下代码:

    from django.template import Context, loader
    from polls.models import Poll
    from django.http import HttpResponse
    def index(request):
        pollList=Poll.objects.all().order_by('-pub_date')[:5]
        t=loader.get_template('polls/index.html')
        c=Context({'pollList':pollList})
        return HttpResponse(t.render(c))

    通过loader加载模板,通过Context将数据赋给模板,这样以后,再访问/polls/就可以看到:

    捕获

    下面再介绍下URL配置的简化和优化,urlpatterns的第一个参数是正则表达式的公共部分,例如这个例子中的url.py可以改为:

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

    python中的app的url映射规则还可以独立开来,可以在polls下新建一个urls.py,其中描述了polls的映射规则:

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

    在mysite目录下的urls.py中,使用include来把这个文件包括进来:

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

    对于使用include的元组,系统找到匹配其正则式的部分,然后把剩余部分转交给include进来的文件中的元组处理。这样可以使得每个app的url映射规则中没有全局url的部分,更加方便复用。

    接下来来完成Poll的详细页面。类似的,我们需要一个template文件如下:

     <h1 >{{poll.question}} </h1 >
    {% if error_message %}
             <p >{{ error_message }} </p >
    {% endif %}
     <form action="/polls/{{poll.id}}/vote" method="post" >
    {% csrf_token %}
    {% for choice in poll.choice_set.all %}
         <input type="radio" name="choice" id="choice{{forloop.counter}}" value="{{choice.id}}"/ >
         <label for="choice{{forloop.counter}}" >{{choice.choice}} </label > <br/ >
    {% endfor %}
     <input type="submit" value="vote"/ >
     </form >

    这个template文件很简单,就是根据一个Poll,显示它的choice。再实现它的view中对应的方法,和index页面是类似的,不过这里采用一种更加简单的写法,为此需要引入:

    from django.shortcuts import *

    方法如下:

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

    get_object_or_404方法可以获得一个对象,如果没有找到,则导到404页面。这样就可以访问detail页面了。但是vote按钮还没实现,现在来实现vote的代码:

    def vote(request,poll_id):
        p=get_object_or_404(Poll,pk=poll_id)
        try:
            selected=p.choice_set.get(pk=request.POST['choice'])
        except (KeyError,Choice.DoesNotExist):
            return render_to_response('polls/detail.html',{'poll':p, 'error_message':"Please Choose one option."},
                                      context_instance=RequestContext(request))
        else:
            selected.votes+=1
            selected.save()
            return HttpResponseRedirect(reverse('polls.views.results',args=(p.id,)))

    利用request.POST对象可以获得post参数。添加了vote方法之后,点击vote按钮会报错,这是由于django的安全机制造成的,在details页面中的form内,有一个{% csrf_token %},是用来防止跨站请求伪造的,这是一个和sessionID有关的值,需要通过request对象来获得,通常,在template页面中无法得到request对象,为此,需要将detail页面的改为:

    def detail(request, poll_id):
        p=get_object_or_404(Poll,pk=poll_id)
        return render_to_response('polls/detail.html',{'poll':p},context_instance=RequestContext(request))

    RequestContext位于django.template模块中。csrf_token的具体原理参考RequestContext

    vote方法的最后,是将页面重定向到结果显示页面。其中reverse 方法可以避免直接拼接url。最后完成results的模板和方法:

     <h1 >{{poll.question}} </h1 >
     <ul >
    {% for choice in poll.choice_set.all %}
         <li >{{choice.choice}}--{{choice.votes}} vote{{choice.votes|pluralize}} </li >
    {% endfor %}
     </ul >
     <a href="/polls/{{poll.id}}/" >Vote again? </a >

    results方法:

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

    至此一个基本的投票系统已经完成,点击vote,可以看到如下页面:

    捕获

    注意到results和detail这两个view非常简单,而且重复。Django有一种Generic View来完成这些简单重复的工作,修改polls/urls.py如下:

    from django.conf.urls.defaults import *
    from django.views.generic import DetailView, ListView
    from polls.models import Poll
    urlpatterns = patterns('',
        (r'^$', ListView.as_view(queryset=Poll.objects.order_by('-pub_date')[:5],
                                 context_object_name='pollList',
                                 template_name='polls/index.html')),
        (r'^(?P\d+)/$', DetailView.as_view(model=Poll,template_name='polls/detail.html')),
        url(r'^(?P\d+)/results/$',
            DetailView.as_view(model=Poll,template_name='polls/results.html'),name='poll_results'),
        (r'^(?P\d+)/vote/$', 'polls.views.vote'),  

    在这里使用了DetailView和ListView两个通用视图。

    PS:这两个视图是在Django 1.3中才引入的,以前的版本写法有些不同。要升级Django要将老版本删掉,方法就是将python/lib/site-packages目录中django相关的内容直接删除,然后再安装新版本。

    这两个View的使用非常简单,只需要把数据源和模板赋给他就可以了。注意到第三条规则中给url取了一个名字poll_results,这是为了给Vote方法中的reverse方法使用的,把vote中的最后一行代码改为:

    return HttpResponseRedirect(reverse('poll_results',args=(p.id,)))

    这样,一个更加简单的投票应用完成了。

  • 相关阅读:
    从0开始学习 GitHub 系列之「02.加入 GitHub」
    从0开始学习 GitHub 系列之「01.初识 GitHub
    用Redis轻松实现秒杀系统
    算法之美
    Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析
    6)django-示例(fbv)
    5)django-模板
    4)django-视图view
    3)django-路由系统url
    2)django-请求生命周期
  • 原文地址:https://www.cnblogs.com/yinzixin/p/2005901.html
Copyright © 2011-2022 走看看