# date: Apr.27,2010
# by fatway#gmail.com
27. Urlconf配置技巧
前面的学习中,在urls.py中进行页面视图配置时,使用的是模块函数导入方法
from mysite.views import hello, current_datetime, hours_ahead
可以将该方法提升到模块级别进行
from django.conf.urls.defaults import *
from mysite import views # 直接导入views模块
urlpatterns = patterns('',
(r'^hello/$', views.hello), # 引用views中的函数
(r'^time/$', views.current_datetime),
(r'^time/plus/(d{1,2})/$', views.hours_ahead),
)
Django还提供一种通过导出函数位置字符串的方式还识别视图
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^hello/$', 'mysite.views.hello'), # 注意是<字符串>形式
(r'^time/$', 'mysite.views.current_datetime'),
)
上面的新方法,还可以将公共部分提取出来
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.views', # 公共部分
(r'^hello/$', 'hello'), # 注意是<字符串>形式
(r'^time/$', 'current_datetime'),
)
当引用多视图时,多个公共前缀可以分别引入
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.views', # 公共部分
(r'^hello/$', 'hello'), # 注意是<字符串>形式
(r'^time/$', 'current_datetime'),
)
urlpatterns += patterns('weblog.views', # 增加patterns对象
(r'^tag/(\w+)/$', 'tag'),
)
调试模式特例
from django.conf import settings
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^$', views.homepage),
(r'^(\d{4})/([a-z]{3})/$', views.archive_month),
)
if settings.DEBUG: # 当settings中debug设置为真时执行
urlpatterns += patterns('',
(r'^debuginfo/$', views.debug),
)
28. Url中使用命名组
无命名组的URLconf
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', views.year_archive),
(r'^articles/(\d{4})/(\d{2})/$', views.month_archive),
)
调用时,请求请求 /articles/2006/03/
相当于month_archive(request, '2006', '03')
有命名组的URLconf
urlpatterns = patterns('',
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive),
)
调用时,请求请求 /articles/2006/03/
相当于month_archive(request, year='2006', month='03')
好处是可以自定义顺序
# date: Apr.29,2010
29. 向视图函数中传递额外参数
当两个视图十分类似时(除了模板不同,其它大致一样),可以合并为一个
首先在urls.py中可以配置相当的视图
urlpatterns = patterns('',
(r'^(foo)/$', views.foobar_view),
(r'^(bar)/$', views.foobar_view),
)
然后在视图views.py中进行判别
def foobar_view(request, url):
m_list = MyModel.objects.filter(is_new=True)
if url == 'foo':
template_name = 'template1.html'
elif url == 'bar':
template_name = 'template2.html'
return render_to_response(template_name, {'m_list': m_list})
针对上面的例子,Django提供了一种优雅的解决方案
在URLconf中还可以向其传递第三个参数:指定模板的字典
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^foo/$', views.foobar_view, {'template_name': 'template1.html'}),
(r'^bar/$', views.foobar_view, {'template_name': 'template2.html'}),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel
def foobar_view(request, template_name):
m_list = MyModel.objects.filter(is_new=True)
return render_to_response(template_name, {'m_list': m_list})
30. 伪造捕捉到的URLconf值
当应用要捕捉如下特定日期的url时
/mydata/jan/01/
/mydata/feb/14/
# ...
可以使用conf
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
函数原型为
def my_view(request, month, day):
# ....
但如果想要增加一个url, /mydata/birthday/ 其等价于 /mydata/jun/06
显然上面的urlconf无法捕捉到该url
可以使用参数来捕捉
urlpatterns = patterns('',
(r'^mydata/birthday/$', views.my_view, {'month': 'jan', 'day': '06'}),
(r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$', views.my_view),
)
31. 创建通用视图
通过对类型进行抽象,可以简化一些操作
以这段代码作为例子:
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^events/$', views.event_list),
(r'^blog/entries/$', views.entry_list),
)
# views.py
from django.shortcuts import render_to_response
from mysite.models import Event, BlogEntry
def event_list(request):
obj_list = Event.objects.all()
return render_to_response('mysite/event_list.html', {'event_list': obj_list})
def entry_list(request):
obj_list = BlogEntry.objects.all()
return render_to_response('mysite/blogentry_list.html', {'entry_list': obj_list})
这两个视图做的事情实质上是一样的: 显示一系列的对象。
把它们显示的对象的类型抽象出来:
# urls.py
from django.conf.urls.defaults import *
from mysite import models, views
urlpatterns = patterns('',
(r'^events/$', views.object_list, {'model': models.Event}),
(r'^blog/entries/$', views.object_list, {'model': models.BlogEntry}),
)
# views.py
from django.shortcuts import render_to_response
def object_list(request, model):
obj_list = model.objects.all()
template_name = 'mysite/%s_list.html' % model.__name__.lower()
return render_to_response(template_name, {'object_list': obj_list})
另外需要了解,在同时传递额外参数和捕捉值时,参数的优先级是大于捕捉值的
urlpatterns = patterns('',
(r'^mydata/(?P<id>\d+)/$', views.my_view, {'id': 3}),
)
对上面视图调用,传递结果一直为 /mydata/3/
32. 使用缺省视图参数
可以像函数中使用默认值一样,可views中也可以指定默认的缺省参数
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^blog/$', views.page),
(r'^blog/page(?P<num>\d+)/$', views.page),
)
# views.py
def page(request, num='1'):
# Output the appropriate page of blog entries, according to num.
# ...
33. 从URL中捕获文本
对每个被捕获的参数,将作为字符串来发送,而不管正则式中的模式
(r'^articles/(?P<year>\d{4})/$', views.year_archive),
其中\d{4}捕获4位的年份数值,但传递到后台时是作为字符来识别的。
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^articles/(\d{4})/(\d{2})/(\d{2})/$', views.day_archive),
)
# views.py
import datetime
def day_archive(request, year, month, day):
date = datetime.date(year, month, day) # 抛出TypeError错误
因此 day_archive() 应该这样写才是正确的:
def day_archive(request, year, month, day):
date = datetime.date(int(year), int(month), int(day))
34. 处理请求分支
在一个视图中同时处理GET和POST不是很好的习惯,应分开两个视图函数来处理
# views.py
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
def method_splitter(request, GET=None, POST=None):
if request.method == 'GET' and GET is not None:
return GET(request)
elif request.method == 'POST' and POST is not None:
return POST(request)
raise Http404
def some_page_get(request):
assert request.method == 'GET'
do_something_for_get()
return render_to_response('page.html')
def some_page_post(request):
assert request.method == 'POST'
do_something_for_post()
return HttpResponseRedirect('/someurl/')
# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns('',
(r'^somepage/$', views.method_splitter,
{'GET': views.some_page_get, 'POST': views.some_page_post}),
# 这里是关键
# 向函数视图传递GET或者POST时会调用不同的处理方法
)
可以使用更优雅的方法来处理请求
def method_splitter(request, *args, **kwargs):
get_view = kwargs.pop('GET', None)
post_view = kwargs.pop('POST', None)
if request.method == 'GET' and get_view is not None:
return get_view(request, *args, **kwargs)
elif request.method == 'POST' and post_view is not None:
return post_view(request, *args, **kwargs)
raise Http404
35. 包含其他URLconf
当程序包含多个app时,可以在根urls中包含app的urls即可
# mysite/urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^weblog/', include('mysite.blog.urls')), # 包含blog中的url
(r'^photos/', include('mysite.photos.urls')), # 注意url后不含$符号
(r'^about/$', 'mysite.views.about'),
)
# mysite/bolg/urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^(\d\d\d\d)/$', 'mysite.blog.views.year_detail'),
(r'^(\d\d\d\d)/(\d\d)/$', 'mysite.blog.views.month_detail'),
)
一个被包含的URLconf,它接收任何来自父级URLconf的被捕捉参数
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
# foo.urls.blog中定义的url都会捕捉到username
)
同样,子级urlconf也可以获取父级的指定参数
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
# inner中每个设置的url都会获取blogid=3这个参数
)