(7)Django框架学习-Views,URLconf的进阶用法篇

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中的所有函数中。
也不太好。





posted @ 2012-08-28 20:34  btchenguang  阅读(9194)  评论(0编辑  收藏  举报