>>> from django.utils.regex_helper import normalize >>> bits=normalize(r'^static/(?P<path>.*)$') >>> bits [(u'static/%(path)s', [u'path'])] >>> bits=normalize(r'^static/(?P<path>.*)/(?P<order>.*)$') >>> bits [(u'static/%(path)s/%(order)s', [u'path', u'order'])]
normalize函数返回列表,列表的元素是元组,元组的第一项是字符串,第二项是组名字。
django.conf.url可以导入include,patterns,url等函数
def include(arg, namespace=None, app_name=None): if app_name and not namespace: raise ValueError('Must specify a namespace if specifying app_name.') if app_name: warnings.warn( 'The app_name argument to django.conf.urls.include() is deprecated. ' 'Set the app_name in the included URLconf instead.', RemovedInDjango20Warning, stacklevel=2 ) if isinstance(arg, tuple): # callable returning a namespace hint try: urlconf_module, app_name = arg except ValueError: if namespace: raise ImproperlyConfigured( 'Cannot override the namespace for a dynamic module that provides a namespace' ) warnings.warn( 'Passing a 3-tuple to django.conf.urls.include() is deprecated. ' 'Pass a 2-tuple containing the list of patterns and app_name, ' 'and provide the namespace argument to include() instead.', RemovedInDjango20Warning, stacklevel=2 ) 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可以是字符串,导入该字符串 urlconf_module = import_module(urlconf_module) patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module) app_name = getattr(urlconf_module, 'app_name', app_name) if namespace and not app_name: warnings.warn( 'Specifying a namespace in django.conf.urls.include() without ' 'providing an app_name is deprecated. Set the app_name attribute ' 'in the included module, or pass a 2-tuple containing the list of ' 'patterns and app_name instead.', RemovedInDjango20Warning, stacklevel=2 ) namespace = namespace or app_name # 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) def patterns(prefix, *args):#返回列表 warnings.warn( 'django.conf.urls.patterns() is deprecated and will be removed in ' 'Django 1.10. Update your urlpatterns to be a list of ' 'django.conf.urls.url() instances instead.', RemovedInDjango110Warning, stacklevel=2 ) 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 def url(regex, view, kwargs=None, name=None, prefix=''): if isinstance(view, (list, tuple)):#返回RegexURLResolver对象 # 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): warnings.warn( 'Support for string view arguments to url() is deprecated and ' 'will be removed in Django 1.10 (got %s). Pass the callable ' 'instead.' % view, RemovedInDjango110Warning, stacklevel=2 ) 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)#返回RegexURLPattern
class RegexURLResolver(LocaleRegexProvider): def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None): print "i am regxurlresolver" LocaleRegexProvider.__init__(self, regex) # urlconf_name is the dotted Python path to the module defining # urlpatterns. It may also be an object with an urlpatterns attribute # or urlpatterns itself. self.urlconf_name = urlconf_name self.callback = None self.default_kwargs = default_kwargs or {} self.namespace = namespace self.app_name = app_name self._reverse_dict = {} self._namespace_dict = {} self._app_dict = {} # set of dotted paths to all functions and classes that are used in # urlpatterns self._callback_strs = set() self._populated = False def __repr__(self): if isinstance(self.urlconf_name, list) and len(self.urlconf_name): # Don't bother to output the whole list, it can be huge urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__ else: urlconf_repr = repr(self.urlconf_name) return str('<%s %s (%s:%s) %s>') % ( self.__class__.__name__, urlconf_repr, self.app_name, self.namespace, self.regex.pattern) def _populate(self):#初始化时会递归调用 lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns):#urls.py的urlpattern if hasattr(pattern, '_callback_str'):#回调字符串集可以用来判断是否可回调 self._callback_strs.add(pattern._callback_str) elif hasattr(pattern, '_callback'):#回调对象转换为字符串 callback = pattern._callback if isinstance(callback, functools.partial): callback = callback.func if not hasattr(callback, '__name__'): lookup_str = callback.__module__ + "." + callback.__class__.__name__ else: lookup_str = callback.__module__ + "." + callback.__name__ self._callback_strs.add(lookup_str) p_pattern = pattern.regex.pattern#取得 RegexURLPattern对象的正则 if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent_pat = pattern.regex.pattern#取得模式字符串 for name in pattern.reverse_dict:#会使得RegexURLResolver对象_populate() for matches, pat, defaults in pattern.reverse_dict.getlist(name): new_matches = normalize(parent_pat + pat)#完善该名字对应的内容 lookups.appendlist( name, ( new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs), ) ) for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) self._callback_strs.update(pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._populated = True
第一次查找视图函数时,会调用_populate,这样会使得各个urls.py模板都解决。
mezzanine的urlresolver
In [27]: url.__dict__
Out[27]:
{'_app_dict': {u'en': {'admin': ['admin']}},
'_callback_strs': {u'django.contrib.auth.admin.user_change_password',
u'django.contrib.auth.views.password_reset',
u'django.contrib.auth.views.password_reset_complete',
u'django.contrib.auth.views.password_reset_confirm',
u'django.contrib.auth.views.password_reset_done',
u'django.contrib.sitemaps.views.sitemap',
u'django.views.i18n.javascript_catalog',
u'filebrowser_safe.decorators.decorator',
u'filebrowser_safe.views._check_file',
u'filebrowser_safe.views.browse',
u'filebrowser_safe.views.delete',
u'filebrowser_safe.views.mkdir',
u'filebrowser_safe.views.rename',
u'filebrowser_safe.views.upload',
u'mezzanine.blog.views.blog_post_detail',
u'mezzanine.blog.views.blog_post_feed',
u'mezzanine.blog.views.blog_post_list',
u'mezzanine.boot.lazy_admin.<lambda>',
u'mezzanine.core.views.direct_to_template',
u'mezzanine.core.views.displayable_links_js',
u'mezzanine.core.views.edit',
u'mezzanine.core.views.search',
u'mezzanine.core.views.set_device',
u'mezzanine.core.views.set_site',
u'mezzanine.core.views.static_proxy',
u'mezzanine.generic.views.admin_keywords_submit',
u'mezzanine.generic.views.comment',
u'mezzanine.generic.views.rating',
u'mezzanine.pages.views.admin_page_ordering',
u'mezzanine.pages.views.page'},
'_namespace_dict': {u'en': {'admin': (u'admin/',
<RegexURLResolver <RegexURLPattern list> (admin:admin) >)}},
'_populated': True,
'_regex': u'^/',
'_regex_dict': {},
'_reverse_dict': {u'en': <MultiValueDict: {u'comment': [([(u'comment/', [])], u
'comment/$', {})], u'rating': [([(u'rating/', [])], u'rating/$', {})], u'admin_k
eywords_submit': [([(u'admin_keywords_submit/', [])], u'admin_keywords_submit/$'
, {})], <function comment at 0x035C8830>: [([(u'comment/', [])], u'comment/$', {
})], u'fb_rename': [([(u'admin/media-library/rename/', [])], u'admin/media-libra
ry/rename/$', {})], u'blog_post_list_month': [([(u'blog/archive/%(year)s/%(month
)s/', [u'year', u'month'])], u'blog/archive/(?P<year>\\d{4})/(?P<month>\\d{1,2})
/$', {})], u'fb_mkdir': [([(u'admin/media-library/mkdir/', [])], u'admin/media-l
ibrary/mkdir/', {})], u'blog_post_list_author': [([(u'blog/author/%(username)s/'
, [u'username'])], u'blog/author/(?P<username>.*)/$', {})], u'set_device': [([(u
'set_device/%(device)s/', [u'device'])], u'set_device/(?P<device>.*)/$', {})], u
'blog_post_list_category': [([(u'blog/category/%(category)s/', [u'category'])],
u'blog/category/(?P<category>.*)/$', {})], <function _check_file at 0x035EA530>:
[([(u'admin/media-library/check_file/', [])], u'admin/media-library/check_file/
$', {})], <function static_proxy at 0x035B0130>: [([(u'asset_proxy/', [])], u'as
set_proxy/$', {})], <function user_change_password at 0x035EA930>: [([(u'admin/a
uth/user/%(_0)s/password/', [u'_0'])], u'admin/auth/user/(\\d+)/password/$', {})
], u'blog_post_detail_day': [([(u'blog/%(year)s/%(month)s/%(day)s/%(slug)s/', [u
'year', u'month', u'day', u'slug'])], u'blog/(?P<year>\\d{4})/(?P<month>\\d{1,2}
)/(?P<day>\\d{1,2})/(?P<slug>.*)/$', {})], u'home': [([(u'', [])], u'$', {u'temp
late': u'index.html'})], u'displayable_links_js': [([(u'displayable_links.js', [
])], u'displayable_links.js$', {})], <function blog_post_list at 0x035C89B0>: [(
[(u'blog/', [])], u'blog/$', {}), ([(u'blog/archive/%(year)s/', [u'year'])], u'b
log/archive/(?P<year>\\d{4})/$', {}), ([(u'blog/archive/%(year)s/%(month)s/', [u
'year', u'month'])], u'blog/archive/(?P<year>\\d{4})/(?P<month>\\d{1,2})/$', {})
, ([(u'blog/author/%(username)s/', [u'username'])], u'blog/author/(?P<username>.
*)/$', {}), ([(u'blog/category/%(category)s/', [u'category'])], u'blog/category/
(?P<category>.*)/$', {}), ([(u'blog/tag/%(tag)s/', [u'tag'])], u'blog/tag/(?P<ta
g>.*)/$', {})], <function rename at 0x035EA8B0>: [([(u'admin/media-library/renam
e/', [])], u'admin/media-library/rename/$', {})], <function blog_post_detail at
0x0361B770>: [([(u'blog/%(slug)s/', [u'slug'])], u'blog/(?P<slug>.*)/$', {}), ([
(u'blog/%(year)s/%(slug)s/', [u'year', u'slug'])], u'blog/(?P<year>\\d{4})/(?P<s
lug>.*)/$', {}), ([(u'blog/%(year)s/%(month)s/%(slug)s/', [u'year', u'month', u'
slug'])], u'blog/(?P<year>\\d{4})/(?P<month>\\d{1,2})/(?P<slug>.*)/$', {}), ([(u
'blog/%(year)s/%(month)s/%(day)s/%(slug)s/', [u'year', u'month', u'day', u'slug'
])], u'blog/(?P<year>\\d{4})/(?P<month>\\d{1,2})/(?P<day>\\d{1,2})/(?P<slug>.*)/
$', {})], <function direct_to_template at 0x035B0270>: [([(u'', [])], u'$', {u't
emplate': u'index.html'})], <function rating at 0x035C8870>: [([(u'rating/', [])
], u'rating/$', {})], u'blog_post_detail': [([(u'blog/%(slug)s/', [u'slug'])], u
'blog/(?P<slug>.*)/$', {})], <function displayable_links_js at 0x035B0330>: [([(
u'displayable_links.js', [])], u'displayable_links.js$', {})], <function sitemap
at 0x035EAB70>: [([(u'sitemap.xml', [])], u'sitemap\\.xml$', {u'sitemaps': {u'a
ll': <class 'mezzanine.core.sitemaps.DisplayableSitemap'>}})], <function mkdir a
t 0x035EA3F0>: [([(u'admin/media-library/mkdir/', [])], u'admin/media-library/mk
dir/', {})], <function search at 0x035B03B0>: [([(u'search/', [])], u'search/$',
{})], u'blog_post_detail_year': [([(u'blog/%(year)s/%(slug)s/', [u'year', u'slu
g'])], u'blog/(?P<year>\\d{4})/(?P<slug>.*)/$', {})], u'blog_post_list_tag': [([
(u'blog/tag/%(tag)s/', [u'tag'])], u'blog/tag/(?P<tag>.*)/$', {})], u'user_chang
e_password': [([(u'admin/auth/user/%(_0)s/password/', [u'_0'])], u'admin/auth/us
er/(\\d+)/password/$', {})], u'blog_post_list': [([(u'blog/', [])], u'blog/$', {
})], u'blog_post_feed_tag': [([(u'blog/tag/%(tag)s/feeds/%(format)s/', [u'tag',
u'format'])], u'blog/tag/(?P<tag>.*)/feeds/(?P<format>.*)/$', {})], <function bl
og_post_feed at 0x0361BC70>: [([(u'blog/author/%(username)s/feeds/%(format)s/',
[u'username', u'format'])], u'blog/author/(?P<username>.*)/feeds/(?P<format>.*)/
$', {}), ([(u'blog/category/%(category)s/feeds/%(format)s/', [u'category', u'for
mat'])], u'blog/category/(?P<category>.*)/feeds/(?P<format>.*)/$', {}), ([(u'blo
g/tag/%(tag)s/feeds/%(format)s/', [u'tag', u'format'])], u'blog/tag/(?P<tag>.*)/
feeds/(?P<format>.*)/$', {}), ([(u'blog/feeds/%(format)s/', [u'format'])], u'blo
g/feeds/(?P<format>.*)/$', {})], u'fb_browse': [([(u'admin/media-library/browse/
', [])], u'admin/media-library/browse/$', {})], <function admin_keywords_submit
at 0x035C87F0>: [([(u'admin_keywords_submit/', [])], u'admin_keywords_submit/$',
{})], u'blog_post_feed_author': [([(u'blog/author/%(username)s/feeds/%(format)s
/', [u'username', u'format'])], u'blog/author/(?P<username>.*)/feeds/(?P<format>
.*)/$', {})], u'blog_post_feed_category': [([(u'blog/category/%(category)s/feeds
/%(format)s/', [u'category', u'format'])], u'blog/category/(?P<category>.*)/feed
s/(?P<format>.*)/$', {})], u'search': [([(u'search/', [])], u'search/$', {})], <
function page at 0x02A92CF0>: [([(u'%(slug)s/', [u'slug'])], u'(?P<slug>.*)/$',
{})], <function set_device at 0x035B00B0>: [([(u'set_device/%(device)s/', [u'dev
ice'])], u'set_device/(?P<device>.*)/$', {})], u'password_reset_done': [([(u'pas
sword_reset/done/', [])], u'password_reset/done/$', {})], u'fb_check': [([(u'adm
in/media-library/check_file/', [])], u'admin/media-library/check_file/$', {})],
<function admin_page_ordering at 0x02A92D30>: [([(u'admin_page_ordering/', [])],
u'admin_page_ordering/$', {})], u'fb_upload': [([(u'admin/media-library/upload/
', [])], u'admin/media-library/upload/', {})], u'blog_post_detail_month': [([(u'
blog/%(year)s/%(month)s/%(slug)s/', [u'year', u'month', u'slug'])], u'blog/(?P<y
ear>\\d{4})/(?P<month>\\d{1,2})/(?P<slug>.*)/$', {})], <function browse at 0x035
EA2F0>: [([(u'admin/media-library/browse/', [])], u'admin/media-library/browse/$
', {})], u'static_proxy': [([(u'asset_proxy/', [])], u'asset_proxy/$', {})], <fu
nction upload at 0x035EA4F0>: [([(u'admin/media-library/upload/', [])], u'admin/
media-library/upload/', {})], <function delete at 0x035EA7B0>: [([(u'admin/media
-library/delete/', [])], u'admin/media-library/delete/$', {})], <function passwo
rd_reset at 0x02A925F0>: [([(u'password_reset/', [])], u'password_reset/$', {})]
, <function set_site at 0x035B0030>: [([(u'set_site/', [])], u'set_site/$', {})]
, u'password_reset_complete': [([(u'reset/done/', [])], u'reset/done/$', {})], <
function edit at 0x035B0430>: [([(u'edit/', [])], u'edit/$', {})], u'blog_post_l
ist_year': [([(u'blog/archive/%(year)s/', [u'year'])], u'blog/archive/(?P<year>\
\d{4})/$', {})], <function javascript_catalog at 0x03571870>: [([(u'jsi18n/%(pac
kages)s/', [u'packages'])], u'jsi18n/(?P<packages>\\S+?)/$', {u'domain': u'djang
o'})], <function password_reset_done at 0x02A92670>: [([(u'password_reset/done/'
, [])], u'password_reset/done/$', {})], u'edit': [([(u'edit/', [])], u'edit/$',
{})], u'password_reset_confirm': [([(u'reset/%(uidb64)s/%(token)s/', [u'uidb64',
u'token'])], u'reset/(?P<uidb64>[0-9A-Za-z_\\-]+)/(?P<token>.+)/$', {})], <func
tion decorator at 0x035EA670>: [([(u'admin/media-library/upload_file/', [])], u'
admin/media-library/upload_file/$', {})], u'fb_do_upload': [([(u'admin/media-lib
rary/upload_file/', [])], u'admin/media-library/upload_file/$', {})], u'fb_delet
e': [([(u'admin/media-library/delete/', [])], u'admin/media-library/delete/$', {
})], <function <lambda> at 0x035B06F0>: [([(u'admin/media-library/', [])], u'adm
in/media-library/$', {})], u'password_reset': [([(u'password_reset/', [])], u'pa
ssword_reset/$', {})], u'media-library': [([(u'admin/media-library/', [])], u'ad
min/media-library/$', {})], u'blog_post_feed': [([(u'blog/feeds/%(format)s/', [u
'format'])], u'blog/feeds/(?P<format>.*)/$', {})], u'admin_page_ordering': [([(u
'admin_page_ordering/', [])], u'admin_page_ordering/$', {})], u'set_site': [([(u
'set_site/', [])], u'set_site/$', {})], <function password_reset_confirm at 0x02
A927B0>: [([(u'reset/%(uidb64)s/%(token)s/', [u'uidb64', u'token'])], u'reset/(?
P<uidb64>[0-9A-Za-z_\\-]+)/(?P<token>.+)/$', {})], u'page': [([(u'%(slug)s/', [u
'slug'])], u'(?P<slug>.*)/$', {})], <function password_reset_complete at 0x02A92
7F0>: [([(u'reset/done/', [])], u'reset/done/$', {})]}>},
'app_name': None,
'callback': None,
'default_kwargs': {},
'namespace': None,
'url_patterns': [<RegexURLResolver <RegexURLPattern list> (None:None) ^admin/>,
<RegexURLPattern home ^$>,
<RegexURLResolver <module 'mezzanine.urls' from 'c:\python27\lib\site-packages
\mezzanine\urls.pyc'> (None:None) ^>],
'urlconf_module': <module 'mez.urls' from 'C:\mez\mez\urls.pyc'>,
'urlconf_name': u'mez.urls'}