Django中URL的解析和反查

add by zhj:

如果想用reverse(namespace1:namespace2:...:namespaceN:name)反查url(注意:用reverse获取到的是path,要自己加上domain才是完整的url),那要做下面两点。

1. 该url路径上用到的所有url()方法中必须加name参数,如果url()方法中有include(),那不用加name参数。

    name相当于文件,同一目录下的文件不能重名,即任何一个patterns(),它里面的各个url()的name参数不能相同。

2. 该url路径上用到的所有include()方法中必须namespace参数。

    include相当于目录,同一父目录下的目录不能重名,即任何一个patterns(),它里面的各个url()中的include()的namespace不能重名

 

下面是正文

1. URL的解析

该部分来自http://blog.csdn.net/hackerain/article/details/40701099

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

urlpatterns = patterns('',
    url(r'^articles/2003/$', 'news.views.special_case_2003'),
    url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
)

urlpatterns += patterns('',
    url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
    url(r'model/', include('model_test.urls')),
)

这段代码就是一个URL Dispatcher的例子,它定义了URL和View之间的映射。在Django中的主配置文件中,用ROOT_URLCONF配置项来指定根URLconf,从根URLconf开始,逐条进行匹配,直到找到匹配项为止。

在上面例子中,我们可以看到有3个方法:patterns, url, include。url方法构建了一个URL到View方法的映射关系对象,patterns将这些映射关系对象组织成为一个python的列表,那include是做什么的呢?它就是我们上面说到的“树”结构关系的联系者,include会关联其他的URLconf到本URLconf,也就是说include关联的是孩子节点。整个URL dispatcher体系,就是由这三个方法构建起来的,下面我们重点来介绍这三个方法,了解了这三个方法,整个URL映射机制就会非常清楚了。

def url(regex, view, kwargs=None, name=None, prefix=''):
    pass

def patterns(prefix, *args):
    pass

def include(arg, namespace=None, app_name=None):
    pass

 

url()

先来看下最重要的url()方法。第一个参数regex是代表URL的正则表达式,第二个参数指定了和该正则表达式映射的View,此外,还可以通过kwargs参数,给view方法指定默认的kwargs参数,还有name参数,用来命名该URL,主要用在URL反解中,至于prefix用处不大,不解释。

url()方法最终构造了一个对象,我们姑且叫它URL映射对象,当第一次访问这个对象去匹配URL时,它会把这个对象中的正则表达式编译一次,然后保存在该对象中,所以以后再次匹配时,就会很快,不会重复编译该正则表达式了。在这里正则匹配其实就是用就是python的re模块,使用过程大致如下:

# 第一次访问时,编译,然后保存在url对象中
regex = re.compile(regex_str, re.UNICODE)

# 每次URL访问时,进行正则匹配
match = regex.search(path)
kwargs = match.groupdict()
args = match.groups()

从URL中获取参数,其实是通过re模块中named groups和non-named groups的概念来获取的,通过match.groupdict()得到的是named groups,其实就是一个字典,字典的key是在URL中指定的,该字典会作为kwargs参数传递给view,而通过match.groups()得到的是non-named groups,是一个元组,即tuple,该元组会作为args参数传递给view。不过,这里的args和kwargs是不能够同时存在的,当有kwargs不为空时,args就会被置空,当kwargs为空时,args才会被用到,而传递给view的kwargs就只有url()方法中指定的默认kwargs。也就是说,如果你在URL中使用了named groups,那么non-named groups就会被忽略,如果只使用了non-named groups,它才会被作为args参数,传递给view方法。

解析一下我们上面提到的例子,假如我们有url和view:

urls:

url(r'^articles/(\d{4})/$', 'news.views.year_archive')
url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive')

views:

def year_archive(request, *args, **kwargs):
    pass

def month_archive(request, *args, **kwargs):
    pass

当我们访问”articles/2014/”这个路径的时候,解析的过程如下:

>>> import re
>>> regex = re.compile(r'^articles/(\d{4})/$', re.UNICODE)
>>> match = regex.search("articles/2014/")
>>> match.groupdict()
{}
>>> match.groups()
('2014',)

当我们访问”articles/2014/11”这个路径时,解析的过程如下:

>>> import re
>>> regex = re.compile(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', re.UNICODE)
>>> match = regex.search("articles/2014/11/")
>>> match.groupdict()
{'year': '2014', 'month': '11'}
>>> match.groups()
('2014', '11')

所以最终传递给month_archive()方法中的参数应该是这样的:args=(),kwargs = {'month': u'11', 'year': u'2014'}

再罗嗦一句,因为url()可以指定一个kwargs参数,它是该url关联的view()方法的默认kwargs参数,也就是说如果在url()方法中指定了kwargs,那么会将这个参数的内容,也传递到view方法中的kwargs参数中。

好,至此,url()方法基本上就清楚了,第二个问题也解决了,至于name参数,到下面讲到URL反解的时候再详细解释。

patterns()

接下来,我们来看patterns()方法,这个其实比较简单,它就是返回一个由url()方法构造的URL映射对象组成的列表。它有一个必填参数是prefix,这个prefix是它所包含的view的公共前缀,这么做是为了避免代码重复,比如:

urlpatterns = patterns('',
    url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
    url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
    url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)

可以写成:

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'),
)

注意,由patterns()生成的列表,被赋值给urlpatterns这个变量,这个变量是不能随便定义的,必须是约定好的,默认django会去URLconf中查找这个变量,也许你可以在某个地方设定一个参数,来换个约定,改变一下这个变量名。

include()

看上面的例子就可以了。

 

2. URL的反查

如果没有下面这种情况,即同一个urls.py模块被多个域名使用。那在写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')),
)

ok,那我们先不考虑上面这种情况,因为出现的概率很小,而且出现了再修改原有的url也可以,影响不大。

如果想对某个url进行反查,那要做两点(其实不是必须,但最好这样做,这样也便于对其它url进行反查,名称空间),

我们把include和name跟文件系统中的目录和文件做比较

1. 该url路径上用到的所有url()方法中必须加name参数,如果url()方法中有include(),那不用加name参数。

    name相当于文件,同一目录下的文件不能重名,即任何一个patterns(),它里面的各个url()的name参数不能相同。

2. 该url路径上用到的所有include()方法中必须namespace参数。

    include相当于目录,同一父目录下的目录不能重名,即任何一个patterns(),它里面的各个url()中的include()的namespace不能重名

当每个url都这样做时,就可以保证每个url在命名空间中可以唯一定位到。见下面例子

urls.py

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

urlpatterns = patterns('',
    url(r'^polls/', include('polls.urls', namespace='polls')),
)

 

polls/urls.py

from django.conf.urls import url, patterns

from . import views

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

在python代码中,用下面的代码就能得到该url的path='/polls/index/',即相对于域名的路径。

include()方法是可以嵌套的,用类似reverse('polls:author_polls:index')可以得到路径

reverse('polls:index')

在模板中,用下面的代码获取

{% url 'polls:index' %}

 

参考:

http://blog.csdn.net/hackerain/article/details/40701099

http://python.usyiyi.cn/django/topics/http/urls.html

你可能感兴趣的:(Django中URL的解析和反查)