• Django框架Views,URLconf的advanced用法


    Views,URLconf的advanced用法

    之前有介绍了一些views和路径匹配的基础用法,在这里介绍一些关于它们的advanced用法。

    URLconf技巧

    因为urls.py也是一个python文件,所以你可以在这个文件中使用python允许的任何语法。
    先看之前介绍的例子:
    from django.conf.urls import patterns, include, url
    from books.views import hello, search_form, search, contact, thanks

    urlpatterns = patterns('',
        url(r'^hello/$', hello),
        url(r'^search/$'search),
        url(r'^contact/$', contact),
        url(r'^contact/thanks/$', thanks),
    )
    每一个路径匹配需要导入相应的模块,所以当app的规范越来越大的时候,第二行的import语句
    就越长。。。所以可以采用以下几种方法来必定:
    1. 只import到模块就可以了,不用写出函数
    from django.conf.urls import patterns, include, url
    from books import views
    urlpatterns = patterns('',
        url(r'^hello/$', views.hello),
        url(r'^search/$', views.search),
        url(r'^contact/$', views.contact),
        url(r'^contact/thanks/$', views.thanks),
    )
    2. 使用字符串来代表需要执行的views函数,这里就需要写全路径
    from django.conf.urls import patterns, include, url

    urlpatterns = patterns('',
        url(r'^hello/$''books.views.hello'),
        url(r'^search/$''books.views.search'),
        url(r'^contact/$','books.views.contact'),
        url(r'^contact/thanks/$''books.views.thanks'),
    )
    3. 更简单的字符串写法,需要使用到patterns()的第一个参数
    from django.conf.urls import patterns, include, url

    urlpatterns = patterns('books.views',
        url(r'^hello/$''hello'),
        url(r'^search/$''search'),
        url(r'^contact/$','contact'),
        url(r'^contact/thanks/$''    thanks'),
    )
    以上的方法都是合法的,具体看你选择了。
     
    因为patterns()返回的对象可以进行加法,当遇到多个views模块的路径前缀时,可以使用如下方式
    来统一管理你的路径匹配:
    from django.conf.urls import patterns, include, url

    urlpatterns = patterns('books.views',
        url(r'^hello/$''hello'),
        url(r'^search/$''search'),
        url(r'^contact/$','contact'),
        url(r'^contact/thanks/$''    thanks'),
    )

    urlpatterns += patterns('weblogs.views',
        url(r'^hello/$''world'),
    )

    使用正规表达式的组操作来向views传递参数

    正则表达式可以使用()来表示匹配成功的组,同时可以使用后向引用\1,\2...\n来表示不同的组,
    在表达式中代表重复的匹配字串。
    不用groups的话,返回的一个match对象。
     
    在python中还可以为组命名,使用(?P<year>\d{4}),意思是这个组的名字为year,匹配的是4个数字
    正因为有命名组和不命名组的这两种使用,正好可以以两种方式给view函数传递参数:
    1. 不命名组以顺序的方式传值
    # views.py
    def count(self, a, b):
        ....
    #urls.py
    (r'^num/(\d{4})/(\d{2})/$', count),
    #结果
    #对于请求/num/1234/78,产生的调用为count(1234, 78)
    2. 命名组以字典的方式传值
    #urls.py
    (r'^num/(?P<b>\d{4})/(?P<a>\d{2})/$', count),
    #结果
    #对于请求/num/1234/78,产生的调用为count(b=1234, a=78)
    通过比较,使用命名组有更大的优势,可以让代码一目了然,也不用担心参数的顺序搞错。
    还是最好不用混用命名组和非命名组,虽然Django不会报错,但不太好,你懂的。
     
    下面了解一下url匹配的过程:
    1. 如果含有命名组,优先采用字典参数
    2. 其余的非命名组将会以顺序的方式传递到剩余的参数中
    3. url中其它的选项,将会以字典的方式传递
    #urls.py
    urlpatterns = patterns('',
        #第三个选项值可用来传递一些附加信息,同时可以让请求路径简洁一点
        #否则有时候请求链接带太多参数也不太好
        (r'^foo/(?P<a>\d{2})$', count, {'b': 43}),
    )
    #结果
    #对于请求/num/12,产生的调用为count(12, b=43)
    当然,view的函数可以使用默认值,这样,如果请求路径中没有匹配到值的话,会调用默认的值。
    注意,一般来说默认值应该设置为字符串,这样是和请求路径匹配过来的值的类型保持一致。因为
    它们都是字符串。即使你匹配的是数学,传入的还是数字的字符串,需要使用int()函数进行转化成
    真正的整数。
     

    在这里复习一下,django到底会匹配请求路径的哪一部分?

    1. 给定请求路径www.example.com/myapp/foo
    只会匹配myapp/foo这个部分
     
    2. 给定请求路径www.example.com/myapp/foo?name=david
    只会匹配myapp/foo这个部分
     
    3. 给定请求路径www.example.com/myapp/foo#name=david
    只会匹配myapp/foo这个部分
     
    请求路径中也不会包含请求GET还是POST,任何匹配成功的请求,都会运行同一个函数,
    但这样做不太好,最好还是对GET和POST采用不同的处理,这里就要用到request对象,
    之前也介绍过,它包含了丰富的信息。
    def foo(request):
        if request.method == 'POST':
            #handle post ...
            #need to redirect
            return HttpResponseRedirect('/someurl/')
        elif request.method == 'GET':
            #handle get
            #just return response
            return render_to_response('page.html')

    通过路径匹配动态构造view中的函数

    上代码:
    #views.py
    def say_hello(self, person_name):
        print 'Hello, %s' % person_name

    def say_goodbye(self, person_name):
        print 'Goodbye, %s' % person_name
    #urls.py
    urlpatterns = patterns('',
        (r'^say_hello/(\w+)$', say_hello),
        (r'^say_goodbye/(\w+)/$', say_goodbye),
    )
    上面两个函数基本是做同一件事,传入一个人的名字,然后问候一句。
    所以稍微改动一下匹配规则,创建一个动态处理这两件事的一个函数。
    #views.py
    def greet(self, person_name, greeting):
        print '%s, %s' % person_name

    #urls.py
    urlpatterns = patterns('',
        (r'^say_hello/(\w+)$', greet, {'greeting':'Hello'}),
        (r'^say_goodbye/(\w+)/$', greet, {'greeing':'Goodbye'}),
    )
    可以看到url中的第三个选项值可以传入额外的信息,保持请求路径的简洁。
    同时,这个选项值还可以传入model类,使用委托的方式,让你的view函数功能更加动态。
    还可以传入模块名称,让你不用把模块名写死在render_to_response函数中。
     
    还有一点需要注意,当命名组的名字和第三个选项值的名字相同时,Django会使用第三个选项值,
    因为它的优先级更高。

    使用闭包和可变长度参数来重构views函数

    这里要用的概念有:
    1. 函数对象也可以当参数进行传递
    2. 闭包,可以简单的理解为是函数中定义函数
    3. 参数中*args代表可变长度元组参数,也叫非关键字参数,**args代表可变长度字典类参数,也叫关键字参数
    注意报错内容,关键字参数一定要放在最右边
     
    重构例子:
    def my_view1(request):
        if not request.user.is_authenticated():
            return HttpResponseRedirect('/accounts/login/')
        # ...
        return render_to_response('template1.html')

    def my_view2(request):
        if not request.user.is_authenticated():
            return HttpResponseRedirect('/accounts/login/')
        # ...
        return render_to_response('template2.html')

    def my_view3(request):
        if not request.user.is_authenticated():
            return HttpResponseRedirect('/accounts/login/')
        # ...
        return render_to_response('template3.html')
    #这上面三个方法,一开始都要进行验证,这有点重复
    #下面添加一个新的方法
    def requires_login(view):
        def new_view(request, *args, **kwargs):
            if not request.user.is_authenticated():
                return HttpResponseRedirect('/accounts/login/')
            return view(request, *args, **kwargs)
        return new_view
    #使用闭包,定义相同的验证的部分,并返回相应的函数对象,这样就可以
    #在上面三个函数中实现自己不同的代码了。可以把验证的部分统统去掉。
    #另外urls.py可以改成
    from django.conf.urls.defaults import *
    from mysite.views import requires_login, my_view1, my_view2, my_view3

    urlpatterns = patterns('',
        (r'^view1/$', requires_login(my_view1)),
        (r'^view2/$', requires_login(my_view2)),
        (r'^view3/$', requires_login(my_view3)),
    )

    使用include()引用其它路径配置文件

    from django.conf.urls.defaults import *

    urlpatterns = patterns('',
        (r'^weblog/', include('mysite.blog.urls')),
        (r'^photos/', include('mysite.photos.urls')),
        (r'^about/$''mysite.views.about'),
    )
    一个project有一个总的urls.py,各个app也可以自己建立自己的urls.py,不过都需要使用include()
    函数在project的urls.py文件进行注册。这样利用你的项目管理。
     
    可以注意到使用了include的路径的匹配表达式没有加上'$',这是因为Django的机制是
    把当前匹配的请求路径的部分去除,剩下的部分传到include所指定的路径配置中进行匹配。
    比如一个请求路径/weblog/7000/过来,匹配部分是weblog/7000/,又因为weblog/匹配成功,
    就把剩余的部分7000/传入到mysit.blog.urls文件中进行下一步的匹配。
     
    当使用include()的路径匹配中含有正则组的时候,这个匹配的参数会传入对include中所指定的urlconf中
    所有的函数里,不管函数是接受这个参数,显然很容易报错,所以这种做法不太好。
     
    同理,如果你在include的路径匹配中使用了第三个选项参数,也会强制传入指定的urlconf中的所有函数中。
    也不太好。





    作者:btchenguang
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    分类: pythonframeworkdjango
    标签: djangoframeworkpython
  • 相关阅读:
    Java多线程系列目录(共43篇)
    Java 集合系列目录(Category)
    Floyd算法(三)之 Java详解
    Floyd算法(二)之 C++详解
    Floyd算法(一)之 C语言详解
    Dijkstra算法(三)之 Java详解
    Dijkstra算法(二)之 C++详解
    Dijkstra算法(一)之 C语言详解
    Prim算法(三)之 Java详解
    Prim算法(二)之 C++详解
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2661041.html
Copyright © 2020-2023  润新知