流线型化(Streamlining) 函 数导入
比较四种方式
1.———————
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime, hours_ahead
urlpatterns = patterns(”,
(r’^hello/$’, hello),
(r’^time/$’, current_datetime),
(r’^time/plus/(/d{1,2})/$’, hours_ahead),
)
2.———————–
from django.conf.urls.defaults import *
**from mysite import views**
urlpatterns = patterns(”,
(r’^hello/$’, **views.hello** ),
(r’^time/$’, **views.current_datetime** ),
(r’^time/plus/(d{1,2})/$’, **views.hours_ahead** ),
)
3.—————————–
from django.conf.urls.defaults import *
urlpatterns = patterns(”,
(r’^hello/$’, **’mysite.views.hello’** ),
(r’^time/$’, **’mysite.views.current_datetime’** ),
(r’^time/plus/(d{1,2})/$’, **’mysite.views.hours_ahead’** ),
)
4.——————-
from django.conf.urls.defaults import *
urlpatterns = patterns(**’mysite.views’** ,
(r’^hello/$’, **’hello’** ),
(r’^time/$’, **’current_datetime’** ),
(r’^time/plus/(d{1,2})/$’, **’hours_ahead’** ),
)
牢记这两种方法, 哪种更好一些呢? 这取决于你的个人编码习惯和需要。
字符串方法的好处如下:
- 更紧凑, 因为不需要你导入视图 函数。
- 如果你的视图函数存在于几个不同的 Python 模块的话, 它可以使得 URLconf 更易读和管理。
函数对象方法的好处如下:
- 更容易对视图函数进行包装(wrap) 。 参见本章后面的《包装视图函数》一节。
- 更 Pythonic, 就是说, 更符合 Python 的传统, 如把函数当成对象传递。
两个方法都是有效的, 甚至你可以 在同一个 URLconf 中混用它 们。 决定权在你。
使用多个视图前缀
from django.conf.urls.defaults import *
urlpatterns = patterns(‘mysite.views’,
(r’^hello/$’, ‘hello’),
(r’^time/$’, ‘current_datetime’),
(r’^time/plus/(/d{1,2})/$’, ‘hours_ahead’),
)
urlpatterns += patterns(‘weblog.views’,
(r’^tag/(/w+)/$’, ‘tag’),
)
整个框架关注的是存在一个名为 urlpatterns 的模块级别的变量。如上例, 这 个变量可以动态生成。 这里我
们要特别说明一下,patterns() 返 回的对象是可相加的, 这个特性可能是大 家没有想到的。
调试模式中的特例
说到动态构建 urlpatterns, 你 可能想利用这一技术, 在 Django 的调试模式下修改 URLconf 的行为。 为
了做到这一点, 只要在运行时检查 DEBUG 配置项的值即可, 如:
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:
urlpatterns += patterns(”,
(r’^debuginfo/$’, views.debug),
)
使用命名组
from django.conf.urls.defaults import *
from mysite import views
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, ‘2006′, ‘03′)
而带命名组, 同样的请求就会变成 这样的函数调用:
month_archive(request, year=’2006′, month=’03′)
理解匹配/ 分组算法
具体地, 以下是URLconf 解释器有关正则表达式中命名组和 非命名组所遵循的算法:
- 如果有任何命名的组,Django 会 忽略非命名组而直接使用命名组。
- 否则,Django 会把所有非 命名组以位置参数的形式传递。
- 在以上的两种情况,Django 同 时会以关键字参数的方式传递一些额外参数。 更具体的信息可参考下一 节。
传递额外的参数到视图函数中
对一个可选URL 配置参数的优雅解决方法: 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})
伪造捕捉到的URLconf 值
比如说你有匹配某个模式的一堆 视图, 以及一个并不匹配这个模式但视图 逻辑是一样的URL 。 这种情况下, 你可以通过向同一个视图传递额外URLconf 参数来伪造URL 值的捕捉。
例如, 你可能有一个显示某一个特 定日子的某些数据的应用,URL 类似这 样的:
/mydata/jan/01/
/mydata/jan/02/
/mydata/jan/03/
# …
/mydata/dec/30/
/mydata/dec/31/
这太简单了, 你可以在一个URLconf 中捕捉这些值, 像这样( 使用命名组的方法):
urlpatterns = patterns(”,
(r’^mydata/(?P<month>/w{3})/(?P<day>/d/d)/$’, views.my_view),
)
然后视图函数的原型看起来会是:
def my_view(request, month, day):
# ….
创建 一个通 用视 图
# 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})
提 供视 图配 置选 项
如果你发布一个Django 的应用, 你的用户可能会希望配置上能有些自由度。 这种情况下, 为你认为用户可能
希望改变的配置选项添加一些钩子到你的视图中会是一个很好的主意。 你可以用额外URLconf 参数实现。
一个应用中比较常见的可供配置代码是模板名字:
def my_view(request, template_name):
var = do_something()
return render_to_response(template_name, {‘var’: var})
使用缺省视图参数
def my_view(request, template_name=’mysite/my_view.html’):
var = do_something()
return render_to_response(template_name, {‘var’: var})
特殊情况下的视图
有时你有一个模式来处理在你的URLconf 中 的一系列URL, 但是有时候需要特别处 理其中的某个URL 。 在这种情况下, 要使用将URLconf 中把特殊情况放在首位的线性处理方式 。
urlpatterns = patterns(”,
# …
(‘^auth/user/add/$’, views.user_add_stage),
(‘^([^/]+)/([^/]+)/add/$’, views.add_stage),
# …
)
从URL 中捕获文本
# 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(int(year), int(month), int(day))
注意, 当你传递了一个并不完全包 含数字的字符串时, int() 会抛 出 ValueError 的异常, 不过我们已经避免
了这个错误, 因为在URLconf 的正则表达式中已经确保只有包含数字 的字符串才会传到这个视图函数中。
决定URLconf 搜索的东西
在解析URLconf 时, 请求方法( 例如, POST , GET , HEAD ) 并 不会 被考虑。换而言之, 对于相同的URL 的所有请求方法将被导向到相同的函数中。 因此根据请求方法来处理分支是视图函数的责任。
视图函数的高级概念
说到关于请求方法的分支, 让我们 来看一下可以用什么好的方法来实现它。
# 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_p
# …
)
优化后
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
包装视图函数
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
函数requires_login, 传 入一个视图函数view, 然后返回一个 新的视图函数new_view. 这个新 的视图函数new_view 在函数requires_login 内定义 处理request.user.is_authenticated() 这 个验证, 从而决定是否执行原来的view 函数
现在, 我们可以从views 中去掉if not request.user.is_authenticated() 验证. 我们可以在URLconf 中很容易的用requires_login 来包装实现.
1
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)),
)
包含其他URLconf
在任何时候, 你的URLconf 都可以包含其他URLconf 模块。 对于根目录是基于一系列URL 的站点来说, 这是必要的。 例如下面的,URLconf 包含了其他URLConf:
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’),
)
继续看这个例子, 这里就是被包含 的URLconf mysite.blog.urls :
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’),
)
捕获的参数如何和include() 协 同工作
一个被包含的URLconf 接收 任何来自parent URLconfs 的 被捕获的参数, 比如:
# root urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns(”,
(r’^(?P<username>/w+)/blog/’, include(‘foo.urls.blog’)),
)
# foo/urls/blog.py
from django.conf.urls.defaults import *
urlpatterns = patterns(”,
(r’^$’, ‘foo.views.blog_index’),
(r’^archive/$’, ‘foo.views.blog_archive’),
)
在这个例子中, 被捕获的 username 变量将传递给被包含的 URLconf, 进而传递给那个URLconf 中的 每一个视图函数。
2
注意, 这个被捕获的参数 总是 传递到被包含的URLconf 中的 每一 行, 不管那些行对应的视图是否需 要这些参数。 因此, 这个技术只有在你 确实需要那个被传递的参数的时候才显得有用。
额外的URLconf 如何和include() 协同工作
# urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns(”,
(r’^blog/’, include(‘inner’)),
)
# inner.py
from django.conf.urls.defaults import *
urlpatterns = patterns(”,
(r’^archive/$’, ‘mysite.views.archive’, {‘blogid’: 3}),
(r’^about/$’, ‘mysite.views.about’, {‘blogid’: 3}),
(r’^rss/$’, ‘mysite.views.rss’, {‘blogid’: 3}),
)