Ticket #10061: t10061-r11201.diff
File t10061-r11201.diff, 29.1 KB (added by , 15 years ago) |
---|
-
django/conf/urls/defaults.py
diff --git a/django/conf/urls/defaults.py b/django/conf/urls/defaults.py index 26cdd3e..572a7b0 100644
a b __all__ = ['handler404', 'handler500', 'include', 'patterns', 'url'] 6 6 handler404 = 'django.views.defaults.page_not_found' 7 7 handler500 = 'django.views.defaults.server_error' 8 8 9 include = lambda urlconf_module: [urlconf_module] 9 def include(arg, namespace=None, app_name=None): 10 if type(arg) == tuple: 11 # callable returning a namespace hint 12 if namespace: 13 raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that provides a namespace') 14 urlconf_module, app_name, namespace = arg 15 else: 16 # No namespace hint - use manually provided namespace 17 urlconf_module = arg 18 return (urlconf_module, app_name, namespace) 10 19 11 20 def patterns(prefix, *args): 12 21 pattern_list = [] … … def patterns(prefix, *args): 19 28 return pattern_list 20 29 21 30 def url(regex, view, kwargs=None, name=None, prefix=''): 22 if type(view) == list:31 if type(view) == tuple: 23 32 # For include(...) processing. 24 return RegexURLResolver(regex, view[0], kwargs) 33 urlconf_module, app_name, namespace = view 34 return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace) 25 35 else: 26 36 if isinstance(view, basestring): 27 37 if not view: -
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 8297eca..0545409 100644
a b class ModelAdmin(BaseModelAdmin): 226 226 return self.admin_site.admin_view(view)(*args, **kwargs) 227 227 return update_wrapper(wrapper, view) 228 228 229 info = self. admin_site.name, self.model._meta.app_label, self.model._meta.module_name229 info = self.model._meta.app_label, self.model._meta.module_name 230 230 231 231 urlpatterns = patterns('', 232 232 url(r'^$', 233 233 wrap(self.changelist_view), 234 name='%s admin_%s_%s_changelist' % info),234 name='%s_%s_changelist' % info), 235 235 url(r'^add/$', 236 236 wrap(self.add_view), 237 name='%s admin_%s_%s_add' % info),237 name='%s_%s_add' % info), 238 238 url(r'^(.+)/history/$', 239 239 wrap(self.history_view), 240 name='%s admin_%s_%s_history' % info),240 name='%s_%s_history' % info), 241 241 url(r'^(.+)/delete/$', 242 242 wrap(self.delete_view), 243 name='%s admin_%s_%s_delete' % info),243 name='%s_%s_delete' % info), 244 244 url(r'^(.+)/$', 245 245 wrap(self.change_view), 246 name='%s admin_%s_%s_change' % info),246 name='%s_%s_change' % info), 247 247 ) 248 248 return urlpatterns 249 249 … … class ModelAdmin(BaseModelAdmin): 582 582 'save_on_top': self.save_on_top, 583 583 'root_path': self.admin_site.root_path, 584 584 }) 585 context_instance = template.RequestContext(request, app_name=self.admin_site.name) 585 586 return render_to_response(self.change_form_template or [ 586 587 "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()), 587 588 "admin/%s/change_form.html" % app_label, 588 589 "admin/change_form.html" 589 ], context, context_instance= template.RequestContext(request))590 ], context, context_instance=context_instance) 590 591 591 592 def response_add(self, request, obj, post_url_continue='../%s/'): 592 593 """ … … class ModelAdmin(BaseModelAdmin): 977 978 'actions_on_bottom': self.actions_on_bottom, 978 979 } 979 980 context.update(extra_context or {}) 981 context_instance = template.RequestContext(request, app_name=self.admin_site.name) 980 982 return render_to_response(self.change_list_template or [ 981 983 'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()), 982 984 'admin/%s/change_list.html' % app_label, 983 985 'admin/change_list.html' 984 ], context, context_instance= template.RequestContext(request))986 ], context, context_instance=context_instance) 985 987 986 988 def delete_view(self, request, object_id, extra_context=None): 987 989 "The 'delete' admin view for this model." … … class ModelAdmin(BaseModelAdmin): 1032 1034 "app_label": app_label, 1033 1035 } 1034 1036 context.update(extra_context or {}) 1037 context_instance = template.RequestContext(request, app_name=self.admin_site.name) 1035 1038 return render_to_response(self.delete_confirmation_template or [ 1036 1039 "admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()), 1037 1040 "admin/%s/delete_confirmation.html" % app_label, 1038 1041 "admin/delete_confirmation.html" 1039 ], context, context_instance= template.RequestContext(request))1042 ], context, context_instance=context_instance) 1040 1043 1041 1044 def history_view(self, request, object_id, extra_context=None): 1042 1045 "The 'history' admin view for this model." … … class ModelAdmin(BaseModelAdmin): 1059 1062 'app_label': app_label, 1060 1063 } 1061 1064 context.update(extra_context or {}) 1065 context_instance = template.RequestContext(request, app_name=self.admin_site.name) 1062 1066 return render_to_response(self.object_history_template or [ 1063 1067 "admin/%s/%s/object_history.html" % (app_label, opts.object_name.lower()), 1064 1068 "admin/%s/object_history.html" % app_label, 1065 1069 "admin/object_history.html" 1066 ], context, context_instance= template.RequestContext(request))1070 ], context, context_instance=context_instance) 1067 1071 1068 1072 # 1069 1073 # DEPRECATED methods. -
django/contrib/admin/sites.py
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 6e9ef11..964719e 100644
a b class AdminSite(object): 38 38 login_template = None 39 39 app_index_template = None 40 40 41 def __init__(self, name=None ):41 def __init__(self, name=None, app_name='admin'): 42 42 self._registry = {} # model_class class -> admin_class instance 43 # TODO Root path is used to calculate urls under the old root() method 44 # in order to maintain backwards compatibility we are leaving that in 45 # so root_path isn't needed, not sure what to do about this. 46 self.root_path = 'admin/' 43 self.root_path = None 47 44 if name is None: 48 name = ''45 self.name = 'admin' 49 46 else: 50 name += '_'51 self. name =name47 self.name = name 48 self.app_name = app_name 52 49 self._actions = {'delete_selected': actions.delete_selected} 53 50 self._global_actions = self._actions.copy() 54 51 … … class AdminSite(object): 114 111 name = name or action.__name__ 115 112 self._actions[name] = action 116 113 self._global_actions[name] = action 117 114 118 115 def disable_action(self, name): 119 116 """ 120 117 Disable a globally-registered action. Raises KeyError for invalid names. 121 118 """ 122 119 del self._actions[name] 123 120 124 121 def get_action(self, name): 125 122 """ 126 123 Explicitally get a registered global action wheather it's enabled or 127 124 not. Raises KeyError for invalid names. 128 125 """ 129 126 return self._global_actions[name] 130 127 131 128 def actions(self): 132 129 """ 133 130 Get all the enabled actions as an iterable of (name, func). … … class AdminSite(object): 186 183 187 184 def get_urls(self): 188 185 from django.conf.urls.defaults import patterns, url, include 189 190 186 def wrap(view): 191 187 def wrapper(*args, **kwargs): 192 188 return self.admin_view(view)(*args, **kwargs) … … class AdminSite(object): 196 192 urlpatterns = patterns('', 197 193 url(r'^$', 198 194 wrap(self.index), 199 name=' %sadmin_index' % self.name),195 name='index'), 200 196 url(r'^logout/$', 201 197 wrap(self.logout), 202 name=' %sadmin_logout'),198 name='logout'), 203 199 url(r'^password_change/$', 204 200 wrap(self.password_change), 205 name=' %sadmin_password_change' % self.name),201 name='password_change'), 206 202 url(r'^password_change/done/$', 207 203 wrap(self.password_change_done), 208 name=' %sadmin_password_change_done' % self.name),204 name='password_change_done'), 209 205 url(r'^jsi18n/$', 210 206 wrap(self.i18n_javascript), 211 name=' %sadmin_jsi18n' % self.name),207 name='jsi18n'), 212 208 url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', 213 209 'django.views.defaults.shortcut'), 214 210 url(r'^(?P<app_label>\w+)/$', 215 211 wrap(self.app_index), 216 name=' %sadmin_app_list' % self.name),212 name='app_list') 217 213 ) 218 214 219 215 # Add in each model's views. … … class AdminSite(object): 222 218 url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name), 223 219 include(model_admin.urls)) 224 220 ) 225 return urlpatterns 221 return urlpatterns, self.app_name, self.name 226 222 227 223 def urls(self): 228 224 return self.get_urls() … … class AdminSite(object): 233 229 Handles the "change password" task -- both form display and validation. 234 230 """ 235 231 from django.contrib.auth.views import password_change 236 return password_change(request, 237 post_change_redirect='%spassword_change/done/' % self.root_path) 232 if self.root_path is not None: 233 url = '%spassword_change/done/' % self.root_path 234 else: 235 url = reverse('%s:password_change_done' % self.name) 236 return password_change(request, post_change_redirect=url) 238 237 239 238 def password_change_done(self, request): 240 239 """ … … class AdminSite(object): 362 361 'root_path': self.root_path, 363 362 } 364 363 context.update(extra_context or {}) 364 context_instance = template.RequestContext(request, app_name=self.name) 365 365 return render_to_response(self.index_template or 'admin/index.html', context, 366 context_instance= template.RequestContext(request)366 context_instance=context_instance 367 367 ) 368 368 index = never_cache(index) 369 369 … … class AdminSite(object): 376 376 'root_path': self.root_path, 377 377 } 378 378 context.update(extra_context or {}) 379 context_instance = template.RequestContext(request, app_name=self.name) 379 380 return render_to_response(self.login_template or 'admin/login.html', context, 380 context_instance= template.RequestContext(request)381 context_instance=context_instance 381 382 ) 382 383 383 384 def app_index(self, request, app_label, extra_context=None): … … class AdminSite(object): 419 420 'root_path': self.root_path, 420 421 } 421 422 context.update(extra_context or {}) 423 context_instance = template.RequestContext(request, app_name=self.name) 422 424 return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label, 423 425 'admin/app_index.html'), context, 424 context_instance= template.RequestContext(request)426 context_instance=context_instance 425 427 ) 426 428 427 429 def root(self, request, url): -
django/contrib/admin/templates/admin/base.html
diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html index 8cab439..9525728 100644
a b 23 23 {% block branding %}{% endblock %} 24 24 </div> 25 25 {% if user.is_authenticated and user.is_staff %} 26 <div id="user-tools">{% trans 'Welcome,' %} <strong>{% firstof user.first_name user.username %}</strong>. {% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}<a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}</div> 26 <div id="user-tools"> 27 {% trans 'Welcome,' %} 28 <strong>{% firstof user.first_name user.username %}</strong>. 29 {% block userlinks %} 30 {% url django-admindocs-docroot as docsroot %} 31 {% if docsroot %} 32 <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / 33 {% endif %} 34 {% url admin:password_change as password_change_url %} 35 {% if password_change_url %} 36 <a href="{{ password_change_url }}"> 37 {% else %} 38 <a href="{{ root_path }}password_change/"> 39 {% endif %} 40 {% trans 'Change password' %}</a> / 41 {% url admin:logout as logout_url %} 42 {% if logout_url %} 43 <a href="{{ logout_url }}"> 44 {% else %} 45 <a href="{{ root_path }}logout/"> 46 {% endif %} 47 {% trans 'Log out' %}</a> 48 {% endblock %} 49 </div> 27 50 {% endif %} 28 51 {% block nav-global %}{% endblock %} 29 52 </div> -
django/core/urlresolvers.py
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index 10e97bb..6b41659 100644
a b class RegexURLPattern(object): 139 139 callback = property(_get_callback) 140 140 141 141 class RegexURLResolver(object): 142 def __init__(self, regex, urlconf_name, default_kwargs=None ):142 def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None): 143 143 # regex is a string representing a regular expression. 144 144 # urlconf_name is a string representing the module containing URLconfs. 145 145 self.regex = re.compile(regex, re.UNICODE) … … class RegexURLResolver(object): 148 148 self._urlconf_module = self.urlconf_name 149 149 self.callback = None 150 150 self.default_kwargs = default_kwargs or {} 151 self._reverse_dict = MultiValueDict() 151 self.namespace = namespace 152 self.app_name = app_name 153 self._reverse_dict = None 154 self._namespace_dict = None 155 self._app_dict = None 152 156 153 157 def __repr__(self): 154 return '<%s %s %s>' % (self.__class__.__name__, self.urlconf_name, self.regex.pattern) 155 156 def _get_reverse_dict(self): 157 if not self._reverse_dict: 158 lookups = MultiValueDict() 159 for pattern in reversed(self.url_patterns): 160 p_pattern = pattern.regex.pattern 161 if p_pattern.startswith('^'): 162 p_pattern = p_pattern[1:] 163 if isinstance(pattern, RegexURLResolver): 158 return '<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern) 159 160 def _populate(self): 161 lookups = MultiValueDict() 162 namespaces = {} 163 apps = {} 164 for pattern in reversed(self.url_patterns): 165 p_pattern = pattern.regex.pattern 166 if p_pattern.startswith('^'): 167 p_pattern = p_pattern[1:] 168 if isinstance(pattern, RegexURLResolver): 169 if pattern.namespace: 170 namespaces[pattern.namespace] = (p_pattern, pattern) 171 if pattern.app_name: 172 apps.setdefault(pattern.app_name, []).append(pattern.namespace) 173 else: 164 174 parent = normalize(pattern.regex.pattern) 165 175 for name in pattern.reverse_dict: 166 176 for matches, pat in pattern.reverse_dict.getlist(name): … … class RegexURLResolver(object): 168 178 for piece, p_args in parent: 169 179 new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) 170 180 lookups.appendlist(name, (new_matches, p_pattern + pat)) 171 else: 172 bits = normalize(p_pattern) 173 lookups.appendlist(pattern.callback, (bits, p_pattern)) 174 lookups.appendlist(pattern.name, (bits, p_pattern)) 175 self._reverse_dict = lookups 181 for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): 182 namespaces[namespace] = (p_pattern + prefix, sub_pattern) 183 for app_name, namespace_list in pattern.app_dict.items(): 184 apps.setdefault(app_name, []).extend(namespace_list) 185 else: 186 bits = normalize(p_pattern) 187 lookups.appendlist(pattern.callback, (bits, p_pattern)) 188 lookups.appendlist(pattern.name, (bits, p_pattern)) 189 self._reverse_dict = lookups 190 self._namespace_dict = namespaces 191 self._app_dict = apps 192 193 def _get_reverse_dict(self): 194 if self._reverse_dict is None: 195 self._populate() 176 196 return self._reverse_dict 177 197 reverse_dict = property(_get_reverse_dict) 178 198 199 def _get_namespace_dict(self): 200 if self._namespace_dict is None: 201 self._populate() 202 return self._namespace_dict 203 namespace_dict = property(_get_namespace_dict) 204 205 def _get_app_dict(self): 206 if self._app_dict is None: 207 self._populate() 208 return self._app_dict 209 app_dict = property(_get_app_dict) 210 179 211 def resolve(self, path): 180 212 tried = [] 181 213 match = self.regex.search(path) … … class RegexURLResolver(object): 261 293 def resolve(path, urlconf=None): 262 294 return get_resolver(urlconf).resolve(path) 263 295 264 def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None): 296 def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, app_name=None): 297 print "REVERSE",viewname,app_name 298 parts = viewname.split(':') 299 parts.reverse() 300 view = parts[0] 301 path = parts[1:] 302 265 303 args = args or [] 266 304 kwargs = kwargs or {} 267 305 if prefix is None: 268 306 prefix = get_script_prefix() 269 return iri_to_uri(u'%s%s' % (prefix, get_resolver(urlconf).reverse(viewname, 307 308 resolver = get_resolver(urlconf) 309 resolver._populate() 310 resolved_path = [] 311 while path: 312 ns = path.pop() 313 # Lookup the name to see if it could be an app identifier 314 try: 315 app_list = resolver.app_dict[ns] 316 # Yes! Path part matches an app in the current Resolver 317 if app_name and app_name in app_list: 318 # If we are reversing for a particular app, use that namespace 319 ns = app_name 320 elif ns not in app_list: 321 # The name isn't shared by one of the instances (i.e., the default) 322 # so just pick the first instance as the default. 323 ns = app_list[0] 324 except KeyError: 325 pass 326 327 try: 328 extra, resolver = resolver.namespace_dict[ns] 329 resolved_path.append(ns) 330 prefix = prefix + extra 331 except KeyError, key: 332 if resolved_path: 333 raise NoReverseMatch("%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path))) 334 else: 335 raise NoReverseMatch("%s is not a registered namespace" % key) 336 337 return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view, 270 338 *args, **kwargs))) 271 339 272 340 def clear_url_caches(): -
django/template/context.py
diff --git a/django/template/context.py b/django/template/context.py index 0ccb5fa..e79af4d 100644
a b class ContextPopException(Exception): 9 9 10 10 class Context(object): 11 11 "A stack container for variable context" 12 def __init__(self, dict_=None, autoescape=True ):12 def __init__(self, dict_=None, autoescape=True, app_name=None): 13 13 dict_ = dict_ or {} 14 14 self.dicts = [dict_] 15 15 self.autoescape = autoescape 16 self.app_name = app_name 16 17 17 18 def __repr__(self): 18 19 return repr(self.dicts) … … class RequestContext(Context): 96 97 Additional processors can be specified as a list of callables 97 98 using the "processors" keyword argument. 98 99 """ 99 def __init__(self, request, dict=None, processors=None ):100 Context.__init__(self, dict )100 def __init__(self, request, dict=None, processors=None, app_name=None): 101 Context.__init__(self, dict, app_name=app_name) 101 102 if processors is None: 102 103 processors = () 103 104 else: -
django/template/defaulttags.py
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 7d91cd6..a61e965 100644
a b class URLNode(Node): 367 367 # {% url ... as var %} construct in which cause return nothing. 368 368 url = '' 369 369 try: 370 url = reverse(self.view_name, args=args, kwargs=kwargs )370 url = reverse(self.view_name, args=args, kwargs=kwargs, app_name=context.app_name) 371 371 except NoReverseMatch, e: 372 372 if settings.SETTINGS_MODULE: 373 373 project_name = settings.SETTINGS_MODULE.split('.')[0] 374 374 try: 375 375 url = reverse(project_name + '.' + self.view_name, 376 args=args, kwargs=kwargs )376 args=args, kwargs=kwargs, app_name=context.app_name) 377 377 except NoReverseMatch: 378 378 if self.asvar is None: 379 379 # Re-raise the original exception, not the one with 380 # the path relative to the project. This makes a 380 # the path relative to the project. This makes a 381 381 # better error message. 382 382 raise e 383 383 else: -
new file tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
diff --git a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py new file mode 100644 index 0000000..08b8567
- + 1 from django.conf.urls.defaults import * 2 from namespace_urls import URLObject 3 4 testobj3 = URLObject('test-ns3') 5 6 urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', 7 url(r'^normal/$', 'empty_view', name='inc-normal-view'), 8 url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='inc-normal-view'), 9 10 (r'^test3/', include(testobj3.urls)), 11 (r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')), 12 ) 13 -
new file tests/regressiontests/urlpatterns_reverse/namespace_urls.py
diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py new file mode 100644 index 0000000..db00e49
- + 1 from django.conf.urls.defaults import * 2 3 class URLObject(object): 4 def __init__(self, namespace): 5 self.namespace = namespace 6 7 def urls(self): 8 return patterns('', 9 url(r'^inner/$', 'empty_view', name='urlobject-view'), 10 url(r'^inner/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='urlobject-view'), 11 ), 'testapp', self.namespace 12 urls = property(urls) 13 14 testobj1 = URLObject('test-ns1') 15 testobj2 = URLObject('test-ns2') 16 default_testobj = URLObject('testapp') 17 18 urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', 19 url(r'^normal/$', 'empty_view', name='normal-view'), 20 url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='normal-view'), 21 22 (r'^test1/', include(testobj1.urls)), 23 (r'^test2/', include(testobj2.urls)), 24 (r'^default/', include(default_testobj.urls)), 25 26 (r'^ns-included1/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns1')), 27 (r'^ns-included2/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns2')), 28 29 (r'^included/', include('regressiontests.urlpatterns_reverse.included_namespace_urls')), 30 31 ) -
tests/regressiontests/urlpatterns_reverse/tests.py
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 9def6b2..cb6e22a 100644
a b class ReverseShortcutTests(TestCase): 158 158 res = redirect('/foo/') 159 159 self.assertEqual(res['Location'], '/foo/') 160 160 res = redirect('http://example.com/') 161 self.assertEqual(res['Location'], 'http://example.com/') 162 No newline at end of file 161 self.assertEqual(res['Location'], 'http://example.com/') 162 163 164 class NamespaceTests(TestCase): 165 urls = 'regressiontests.urlpatterns_reverse.namespace_urls' 166 167 def test_ambiguous_object(self): 168 "Names deployed via dynamic URL objects that require namespaces can't be resolved" 169 self.assertRaises(NoReverseMatch, reverse, 'urlobject-view') 170 self.assertRaises(NoReverseMatch, reverse, 'urlobject-view', args=[37,42]) 171 self.assertRaises(NoReverseMatch, reverse, 'urlobject-view', kwargs={'arg1':42, 'arg2':37}) 172 173 def test_ambiguous_urlpattern(self): 174 "Names deployed via dynamic URL objects that require namespaces can't be resolved" 175 self.assertRaises(NoReverseMatch, reverse, 'inner-nothing') 176 self.assertRaises(NoReverseMatch, reverse, 'inner-nothing', args=[37,42]) 177 self.assertRaises(NoReverseMatch, reverse, 'inner-nothing', kwargs={'arg1':42, 'arg2':37}) 178 179 def test_non_existent_namespace(self): 180 "Non-existent namespaces raise errors" 181 self.assertRaises(NoReverseMatch, reverse, 'blahblah:urlobject-view') 182 self.assertRaises(NoReverseMatch, reverse, 'test-ns1:blahblah:urlobject-view') 183 184 def test_normal_name(self): 185 "Normal lookups work as expected" 186 self.assertEquals('/normal/', reverse('normal-view')) 187 self.assertEquals('/normal/37/42/', reverse('normal-view', args=[37,42])) 188 self.assertEquals('/normal/42/37/', reverse('normal-view', kwargs={'arg1':42, 'arg2':37})) 189 190 def test_simple_included_name(self): 191 "Normal lookups work on names included from other patterns" 192 self.assertEquals('/included/normal/', reverse('inc-normal-view')) 193 self.assertEquals('/included/normal/37/42/', reverse('inc-normal-view', args=[37,42])) 194 self.assertEquals('/included/normal/42/37/', reverse('inc-normal-view', kwargs={'arg1':42, 'arg2':37})) 195 196 def test_namespace_object(self): 197 "Dynamic URL objects can be found using a namespace" 198 self.assertEquals('/test1/inner/', reverse('test-ns1:urlobject-view')) 199 self.assertEquals('/test1/inner/37/42/', reverse('test-ns1:urlobject-view', args=[37,42])) 200 self.assertEquals('/test1/inner/42/37/', reverse('test-ns1:urlobject-view', kwargs={'arg1':42, 'arg2':37})) 201 202 def test_embedded_namespace_object(self): 203 "Namespaces can be installed anywhere in the URL pattern tree" 204 self.assertEquals('/included/test3/inner/', reverse('test-ns3:urlobject-view')) 205 self.assertEquals('/included/test3/inner/37/42/', reverse('test-ns3:urlobject-view', args=[37,42])) 206 self.assertEquals('/included/test3/inner/42/37/', reverse('test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37})) 207 208 def test_namespace_pattern(self): 209 "Namespaces can be applied to include()'d urlpatterns" 210 self.assertEquals('/ns-included1/normal/', reverse('inc-ns1:inc-normal-view')) 211 self.assertEquals('/ns-included1/normal/37/42/', reverse('inc-ns1:inc-normal-view', args=[37,42])) 212 self.assertEquals('/ns-included1/normal/42/37/', reverse('inc-ns1:inc-normal-view', kwargs={'arg1':42, 'arg2':37})) 213 214 def test_multiple_namespace_pattern(self): 215 "Namespaces can be embedded" 216 self.assertEquals('/ns-included1/test3/inner/', reverse('inc-ns1:test-ns3:urlobject-view')) 217 self.assertEquals('/ns-included1/test3/inner/37/42/', reverse('inc-ns1:test-ns3:urlobject-view', args=[37,42])) 218 self.assertEquals('/ns-included1/test3/inner/42/37/', reverse('inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37})) 219 220 def test_app_lookup_object(self): 221 "A default application namespace can be used for lookup" 222 self.assertEquals('/default/inner/', reverse('testapp:urlobject-view')) 223 self.assertEquals('/default/inner/37/42/', reverse('testapp:urlobject-view', args=[37,42])) 224 self.assertEquals('/default/inner/42/37/', reverse('testapp:urlobject-view', kwargs={'arg1':42, 'arg2':37})) 225 226 def test_app_lookup_object_with_default(self): 227 "A default application namespace is sensitive to the 'current' app can be used for lookup" 228 self.assertEquals('/included/test3/inner/', reverse('testapp:urlobject-view', app_name='test-ns3')) 229 self.assertEquals('/included/test3/inner/37/42/', reverse('testapp:urlobject-view', args=[37,42], app_name='test-ns3')) 230 self.assertEquals('/included/test3/inner/42/37/', reverse('testapp:urlobject-view', kwargs={'arg1':42, 'arg2':37}, app_name='test-ns3')) 231