django url调度

Django的url配置同样遵循着DRY(dont repeat yourself)的规则。以下都是官方文档的例子:

首先介绍的是Django如何处理http的请求:

1、在setting里定义ROOT_URLCONF ,这个值就是url的根配置,但若被request processing中间件定义了HttpRequest的urlconf属性,会替换掉ROOT_URLCONF
2、Django 加载模块,寻找 urlpatterns,它是pattern函数的返回值,是url的list

3、寻找每个url pattern 直到找到第一个匹配的

4、一旦找到,会调用对应的view函数。view函数必须带有HttpRequest对象(request),若url里没有命名参数,view函数里可以带位置参数,若有命名参数,则view函数里同样有命名参数。同时命名参数还有可能是 django.conf.urls.url()函数里添加的额外关键字参数。

5、若给出的url没有pattern匹配,则触发异常。

如:

from django.conf.urls import patterns, url

from . import views

urlpatterns = patterns('',
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(\d{4})/$', views.year_archive),
    url(r'^articles/(\d{4})/(\d{2})/$', views.month_archive),
    url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', views.article_detail),
)
注意article前不用加 /
括号的作用是捕捉传入url的值给view函数。

最后一个若请求的url是 articles/2003/03/03,调用的view 函数为views.article_detail(request, '2003', '03', '03').

在正则里分组:

url(r'^articles/(?P\d{4})/(?P\d{2})/(?P\d{2})/$', views.article_detail)
则同样的url请求调用的是 views.article_detail(request, year='2003', month='03', day='03') .

django 不会处理传入的域名和查询参数,比如http://www.example.com/myapp/?page=3 只能匹配myapp/

捕捉到的传给view函数的值永远是string类型的,要变成数字则需要转换。

可将不同的url指向同一个view函数,如

    url(r'^blog/$', views.page),
    url(r'^blog/page(?P\d+)/$', views.page),
page函数可以有个默认的参数 num=1 。

性能:每个正则会在第一次被访问的时候被编译,所以速度很快。

错误处理:若不能匹配任何一个正则,或有异常,django会调用错误处理的view函数。需要在ROOT_URLCONF里指定,其他位置无效。

包括

  • handler404 – See django.conf.urls.handler404.
  • handler500 – See django.conf.urls.handler500.
  • handler403 – See django.conf.urls.handler403.
  • handler400 – See django.conf.urls.handler400.
传递string代替view函数:

    url(r'^archive/$', 'mysite.views.archive'),
    url(r'^about/$', 'mysite.views.about'),
    url(r'^contact/$', 'mysite.views.contact'),
有相同的前缀还可以把相同的前缀写到patterns的第一个参数里:

urlpatterns = patterns('news.views',
    url(r'^articles/(\d{4})/$', 'year_archive'),
    url(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
    url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)
多个前缀时可以用urlpatterns+=patterns(...) ,记住url配置也是python代码。

include的用法,常见用法:

from django.conf.urls import include, patterns, url
... 
url(r'^comments/', include('django.contrib.comments.urls')),
    url(r'^community/', include('django_website.aggregator.urls')),
    url(r'^contact/', include('django_website.contact.urls')),
...
include的另一种用法,参数是patterns函数的返回值:

extra_patterns = patterns('',
    url(r'^reports/(?P\d+)/$', credit_views.report),
    url(r'^charge/$', credit_views.charge),
)
urlpatterns = patterns('',
    url(r'^$', main_views.homepage),
    url(r'^help/', include('apps.help.urls')),
    url(r'^credit/', include(extra_patterns)),
)
 url传递的命名参数,位置参数在include里同样有效。 
  

给view函数传递额外参数,在url函数里可以传递一个字典。

    url(r'^blog/(?P\d{4})/$', views.year_archive, {'foo': 'bar'}),
django会调用 views.year_archive(request, year='2005', foo='bar') .

若有同名冲突,即正则分组的名和字典里key的值相同,则取后者。

url反射:

反向获取视图对应的url是个常见的需求。硬编码(把url写死)不是DRY的做法。django在MTV各层分别提供了工具来反射获取url。

模板中: url 模板标签

python代码中,用django.core.urlresolvers.reverse()函数

模型层: get_absolute_url()方法

例如有

url(r'^articles/(\d{4})/$', views.year_archive),
的对应关系。

2012 Archive
{# Or with the year in a template context variable: #}
在python代码中:

year = 2006
return HttpResponseRedirect(reverse('news.views.year_archive', args=(year,)))####参数是tuple
这样当url改变时只改变入口即可。

命名urlpatterns

存在多个url对应同一个view的情况。这样当反射的时候会出现歧义,解决办法是在定义使用url函数时加上 name=参数。

 url(r'^archive/(\d{4})/$', archive, name="full-archive"),
    url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, name="arch-summary"),
{% url 'arch-summary' 1945 %}
{% url 'full-archive' 2007 %}
就分别对应了不同的url。


url命名空间:

url命名空间允许惟一地反射已命名的url,即使不同的app有相同的url名字。

第三方app使用命名空间是很好的习惯。类似地,若一个应用的多个实例部署时,也允许你反射url,因为一个app的多个实例共享相同的url,命名空间提供了一种方法来区分它们。

充分利用了url命名空间的django应用可被特定站点部署多次。例如admin的AdminSite类允许你部署一个admin的多个实例。

url namespace有两部分,分别是application namespace和instance namespace。用冒号分分隔。namespace可以嵌套, 'sports:polls:index'

命名空间url的反射:e.g. 'polls:index'

1、django把命名空间分开,找application namespace,本例为polls。这会产生一个polls 的实例的列表

2、若定义了current application(的实例), django找到并返回这个实例的url 解析。current application被设定为模板上下文的一个属性,期望有多部署的应用应该设定current_app 属性给每个渲染模板的Context或者RequestContext。

3、若无current application,django寻找默认的应用实例,默认的应用实例是有和application namespace相同名称的instance namespace。本例中是polls:polls

4、若没有默认实例,django会跳出最后一次部署的应用实例,不管其instance namespace是什么。

5、若提供的namespace和第一步的application namespace不匹配,django尝试直接寻找这个namespace作为instance namespace。

如:两个polls应用的实例,一个叫author-polls,另一个叫publisher-polls。

urls.py

from django.conf.urls import include, patterns, url

urlpatterns = patterns('',
    url(r'^author-polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
    url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls', app_name='polls')),
)
polls/urls.py

from django.conf.urls import patterns, url

from . import views

urlpatterns = patterns('',
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P\d+)/$', views.DetailView.as_view(), name='detail'),
    ...
)
1、若一个实例是当前实例,即,若准备在实例‘ 'author-polls中渲染datail page, 'polls:index'会解析到 'author-polls实例,也即下面的例子会返回 "/author-polls/"


reverse('polls:index', current_app=self.request.resolver_match.namespace)
{% url 'polls:index' %}

要注意current_app属性要被加到context里,


def render_to_response(self, context, **response_kwargs):
    response_kwargs['current_app'] = self.request.resolver_match.namespace
    return super(DetailView, self).render_to_response(context, **response_kwargs)
2、若无当前实例,即要渲染的页面在站点上的其他页面,   'polls:index'会解析到最后一个注册给polls的实例。因为没有默认的 (instance namespace of  'polls' )。会被解析到   'publisher-polls'


3、'author-polls:index'会一直被解到实例为 'author-polls'的索引页,另一个也一样。

若有polls的默认实例,即实例名为polls,则上面改变的地方在第2步,会被解析为默认的实例而不是最后一个注册的。


url命名空间和 include

两种方法

1、显式指明 application 和instance 的namespace,作为include的参数

url(r'^polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
定义在polls里面的url都被加入了app_name 为 polls和instance为 author-polls的命名空间

2、include可以接受一个tuple作为参数,tuple是三元的,分别是

(, , )

from . import views

polls_patterns = patterns('',
    url(r'^$', views.IndexView.as_view(), name='index'),
    url(r'^(?P\d+)/$', views.DetailView.as_view(), name='detail'),
)

url(r'^polls/', include((polls_patterns, 'polls', 'author-polls'))),
记得一定要传tuple。若仅仅传送的是三个元素,则会与第一种效果一样。但是instance 和application namespace的顺序,两种签名是相反的。django不会报错

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

最后附上 include url patterns的源码

from importlib import import_module

from django.core.urlresolvers import (RegexURLPattern,
    RegexURLResolver, LocaleRegexURLResolver)
from django.core.exceptions import ImproperlyConfigured
from django.utils import six


__all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'patterns', 'url']

handler400 = 'django.views.defaults.bad_request'
handler403 = 'django.views.defaults.permission_denied'
handler404 = 'django.views.defaults.page_not_found'
handler500 = 'django.views.defaults.server_error'


[docs] def include ( arg , namespace = None , app_name = None ): if isinstance ( arg , tuple ): # callable returning a namespace hint if namespace : raise ImproperlyConfigured ( 'Cannot override the namespace for a dynamic module that provides a namespace' ) urlconf_module , app_name , namespace = arg else : # No namespace hint - use manually provided namespace urlconf_module = arg if isinstance ( urlconf_module , six . string_types ): urlconf_module = import_module ( urlconf_module ) patterns = getattr ( urlconf_module , 'urlpatterns' , urlconf_module ) # Make sure we can iterate through the patterns (without this, some # testcases will break). if isinstance ( patterns , ( list , tuple )): for url_pattern in patterns : # Test if the LocaleRegexURLResolver is used within the include; # this should throw an error since this is not allowed! if isinstance ( url_pattern , LocaleRegexURLResolver ): raise ImproperlyConfigured ( 'Using i18n_patterns in an included URLconf is not allowed.' ) return ( urlconf_module , app_name , namespace )
[docs] def patterns ( prefix , * args ): pattern_list = [] for t in args : if isinstance ( t , ( list , tuple )): t = url ( prefix = prefix , * t ) elif isinstance ( t , RegexURLPattern ): t . add_prefix ( prefix ) pattern_list . append ( t ) return pattern_list
[docs] def url ( regex , view , kwargs = None , name = None , prefix = '' ): if isinstance ( view , ( list , tuple )): # For include(...) processing. urlconf_module , app_name , namespace = view return RegexURLResolver ( regex , urlconf_module , kwargs , app_name = app_name , namespace = namespace ) else : if isinstance ( view , six . string_types ): if not view : raise ImproperlyConfigured ( 'Empty URL pattern view name not permitted (for pattern %r )' % regex ) if prefix : view = prefix + '.' + view return RegexURLPattern ( regex , view , kwargs , name )



你可能感兴趣的:(django)