+51
-2
lines changedFilter options
+51
-2
lines changed Original file line number Diff line number Diff line change
@@ -230,6 +230,10 @@ def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, name
230
230
self._reverse_dict = {}
231
231
self._namespace_dict = {}
232
232
self._app_dict = {}
233
+
# set of dotted paths to all functions and classes that are used in
234
+
# urlpatterns
235
+
self._callback_strs = set()
236
+
self._populated = False
233
237
234
238
def __repr__(self):
235
239
return smart_str(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
@@ -240,6 +244,15 @@ def _populate(self):
240
244
apps = {}
241
245
language_code = get_language()
242
246
for pattern in reversed(self.url_patterns):
247
+
if hasattr(pattern, '_callback_str'):
248
+
self._callback_strs.add(pattern._callback_str)
249
+
elif hasattr(pattern, '_callback'):
250
+
callback = pattern._callback
251
+
if not hasattr(callback, '__name__'):
252
+
lookup_str = callback.__module__ + "." + callback.__class__.__name__
253
+
else:
254
+
lookup_str = callback.__module__ + "." + callback.__name__
255
+
self._callback_strs.add(lookup_str)
243
256
p_pattern = pattern.regex.pattern
244
257
if p_pattern.startswith('^'):
245
258
p_pattern = p_pattern[1:]
@@ -260,6 +273,7 @@ def _populate(self):
260
273
namespaces[namespace] = (p_pattern + prefix, sub_pattern)
261
274
for app_name, namespace_list in pattern.app_dict.items():
262
275
apps.setdefault(app_name, []).extend(namespace_list)
276
+
self._callback_strs.update(pattern._callback_strs)
263
277
else:
264
278
bits = normalize(p_pattern)
265
279
lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
@@ -268,6 +282,7 @@ def _populate(self):
268
282
self._reverse_dict[language_code] = lookups
269
283
self._namespace_dict[language_code] = namespaces
270
284
self._app_dict[language_code] = apps
285
+
self._populated = True
271
286
272
287
@property
273
288
def reverse_dict(self):
@@ -356,8 +371,13 @@ def reverse(self, lookup_view, *args, **kwargs):
356
371
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
357
372
if args and kwargs:
358
373
raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
374
+
375
+
if not self._populated:
376
+
self._populate()
377
+
359
378
try:
360
-
lookup_view = get_callable(lookup_view, True)
379
+
if lookup_view in self._callback_strs:
380
+
lookup_view = get_callable(lookup_view, True)
361
381
except (ImportError, AttributeError), e:
362
382
raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))
363
383
possibilities = self.reverse_dict.getlist(lookup_view)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1
+
def view(request):
2
+
"""Stub view"""
3
+
pass
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
1
+
# -*- coding: utf-8 -*-
1
2
"""
2
3
Unit tests for reverse URL lookups.
3
4
"""
4
5
from __future__ import absolute_import
5
6
7
+
import sys
8
+
6
9
from django.conf import settings
7
10
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
8
11
from django.core.urlresolvers import (reverse, resolve, NoReverseMatch,
@@ -267,6 +270,25 @@ def test_redirect_to_url(self):
267
270
self.assertEqual(res['Location'], '/foo/')
268
271
res = redirect('http://example.com/')
269
272
self.assertEqual(res['Location'], 'http://example.com/')
273
+
# Assert that we can redirect using UTF-8 strings
274
+
res = redirect('/æøå/abc/')
275
+
self.assertEqual(res['Location'], '/%C3%A6%C3%B8%C3%A5/abc/')
276
+
# Assert that no imports are attempted when dealing with a relative path
277
+
# (previously, the below would resolve in a UnicodeEncodeError from __import__ )
278
+
res = redirect('/æøå.abc/')
279
+
self.assertEqual(res['Location'], '/%C3%A6%C3%B8%C3%A5.abc/')
280
+
res = redirect('os.path')
281
+
self.assertEqual(res['Location'], 'os.path')
282
+
283
+
def test_no_illegal_imports(self):
284
+
# modules that are not listed in urlpatterns should not be importable
285
+
redirect("urlpatterns_reverse.nonimported_module.view")
286
+
self.assertNotIn("urlpatterns_reverse.nonimported_module", sys.modules)
287
+
288
+
def test_reverse_by_path_nested(self):
289
+
# Views that are added to urlpatterns using include() should be
290
+
# reversable by doted path.
291
+
self.assertEqual(reverse('regressiontests.urlpatterns_reverse.views.nested_view'), '/includes/nested_path/')
270
292
271
293
def test_redirect_view_object(self):
272
294
from .views import absolute_kwargs_view
@@ -510,4 +532,3 @@ def test_erroneous_resolve(self):
510
532
self.assertRaises(ViewDoesNotExist, self.client.get, '/missing_inner/')
511
533
self.assertRaises(ViewDoesNotExist, self.client.get, '/missing_outer/')
512
534
self.assertRaises(ViewDoesNotExist, self.client.get, '/uncallable/')
513
-
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
7
7
8
8
other_patterns = patterns('',
9
9
url(r'non_path_include/$', empty_view, name='non_path_include'),
10
+
url(r'nested_path/$', 'regressiontests.urlpatterns_reverse.views.nested_view'),
10
11
)
11
12
12
13
urlpatterns = patterns('',
Original file line number Diff line number Diff line change
@@ -16,6 +16,10 @@ def absolute_kwargs_view(request, arg1=1, arg2=2):
16
16
def defaults_view(request, arg1, arg2):
17
17
pass
18
18
19
+
def nested_view(request):
20
+
pass
21
+
22
+
19
23
def erroneous_view(request):
20
24
import non_existent
21
25
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4