Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Improved urlresolvers so that URLconfs can be passed objects instead …

…of strings

git-svn-id: http://code.djangoproject.com/svn/django/trunk@3554 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 0b71ffacab025e82f58dfe40d4d8d22540f67471 1 parent 4805675
Adrian Holovaty authored
50  django/core/urlresolvers.py
@@ -86,10 +86,15 @@ def __call__(self, match_obj):
86 86
 class RegexURLPattern(object):
87 87
     def __init__(self, regex, callback, default_args=None):
88 88
         # regex is a string representing a regular expression.
89  
-        # callback is something like 'foo.views.news.stories.story_detail',
90  
-        # which represents the path to a module and a view function name.
  89
+        # callback is either a string like 'foo.views.news.stories.story_detail'
  90
+        # which represents the path to a module and a view function name, or a
  91
+        # callable object (view).
91 92
         self.regex = re.compile(regex)
92  
-        self.callback = callback
  93
+        if callable(callback):
  94
+            self._callback = callback
  95
+        else:
  96
+            self._callback = None
  97
+            self._callback_str = callback
93 98
         self.default_args = default_args or {}
94 99
 
95 100
     def resolve(self, path):
@@ -106,23 +111,28 @@ def resolve(self, path):
106 111
             # In both cases, pass any extra_kwargs as **kwargs.
107 112
             kwargs.update(self.default_args)
108 113
 
109  
-            try: # Lazily load self.func.
110  
-                return self.func, args, kwargs
111  
-            except AttributeError:
112  
-                self.func = self.get_callback()
113  
-            return self.func, args, kwargs
  114
+            return self.callback, args, kwargs
114 115
 
115  
-    def get_callback(self):
116  
-        mod_name, func_name = get_mod_func(self.callback)
  116
+    def _get_callback(self):
  117
+        if self._callback is not None:
  118
+            return self._callback
  119
+        mod_name, func_name = get_mod_func(self._callback_str)
117 120
         try:
118  
-            return getattr(__import__(mod_name, '', '', ['']), func_name)
  121
+            self._callback = getattr(__import__(mod_name, '', '', ['']), func_name)
119 122
         except ImportError, e:
120 123
             raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))
121 124
         except AttributeError, e:
122 125
             raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))
  126
+        return self._callback
  127
+    callback = property(_get_callback)
123 128
 
124 129
     def reverse(self, viewname, *args, **kwargs):
125  
-        if viewname != self.callback:
  130
+        mod_name, func_name = get_mod_func(viewname)
  131
+        try:
  132
+            lookup_view = getattr(__import__(mod_name, '', '', ['']), func_name)
  133
+        except (ImportError, AttributeError):
  134
+            raise NoReverseMatch
  135
+        if lookup_view != self.callback:
126 136
             raise NoReverseMatch
127 137
         return self.reverse_helper(*args, **kwargs)
128 138
 
@@ -185,22 +195,28 @@ def resolve404(self):
185 195
     def resolve500(self):
186 196
         return self._resolve_special('500')
187 197
 
188  
-    def reverse(self, viewname, *args, **kwargs):
  198
+    def reverse(self, lookup_view, *args, **kwargs):
  199
+        if not callable(lookup_view):
  200
+            mod_name, func_name = get_mod_func(lookup_view)
  201
+            try:
  202
+                lookup_view = getattr(__import__(mod_name, '', '', ['']), func_name)
  203
+            except (ImportError, AttributeError):
  204
+                raise NoReverseMatch
189 205
         for pattern in self.urlconf_module.urlpatterns:
190 206
             if isinstance(pattern, RegexURLResolver):
191 207
                 try:
192  
-                    return pattern.reverse_helper(viewname, *args, **kwargs)
  208
+                    return pattern.reverse_helper(lookup_view, *args, **kwargs)
193 209
                 except NoReverseMatch:
194 210
                     continue
195  
-            elif pattern.callback == viewname:
  211
+            elif pattern.callback == lookup_view:
196 212
                 try:
197 213
                     return pattern.reverse_helper(*args, **kwargs)
198 214
                 except NoReverseMatch:
199 215
                     continue
200 216
         raise NoReverseMatch
201 217
 
202  
-    def reverse_helper(self, viewname, *args, **kwargs):
203  
-        sub_match = self.reverse(viewname, *args, **kwargs)
  218
+    def reverse_helper(self, lookup_view, *args, **kwargs):
  219
+        sub_match = self.reverse(lookup_view, *args, **kwargs)
204 220
         result = reverse_helper(self.regex, *args, **kwargs)
205 221
         return result + sub_match
206 222
 
45  docs/url_dispatch.txt
@@ -431,3 +431,48 @@ Note that extra options will *always* be passed to *every* line in the included
431 431
 URLconf, regardless of whether the line's view actually accepts those options
432 432
 as valid. For this reason, this technique is only useful if you're certain that
433 433
 every view in the the included URLconf accepts the extra options you're passing.
  434
+
  435
+Passing callable objects instead of strings
  436
+===========================================
  437
+
  438
+**New in the Django development version.**
  439
+
  440
+Some developers find it more natural to pass the actual Python function object
  441
+rather than a string containing the path to its module. This alternative is
  442
+supported -- you can pass any callable object as the view.
  443
+
  444
+For example, given this URLconf in "string" notation::
  445
+
  446
+    urlpatterns = patterns('',
  447
+        (r'^archive/$', 'mysite.views.archive'),
  448
+        (r'^about/$', 'mysite.views.about'),
  449
+        (r'^contact/$', 'mysite.views.contact'),
  450
+    )
  451
+
  452
+You can accomplish the same thing by passing objects rather than strings. Just
  453
+be sure to import the objects::
  454
+
  455
+    from mysite.views import archive, about, contact
  456
+
  457
+    urlpatterns = patterns('',
  458
+        (r'^archive/$', archive),
  459
+        (r'^about/$', about),
  460
+        (r'^contact/$', contact),
  461
+    )
  462
+
  463
+The following example is functionally identical. It's just a bit more compact
  464
+because it imports the module that contains the views, rather than importing
  465
+each view individually::
  466
+
  467
+    from mysite import views
  468
+
  469
+    urlpatterns = patterns('',
  470
+        (r'^archive/$', views.archive),
  471
+        (r'^about/$', views.about),
  472
+        (r'^contact/$', views.contact),
  473
+    )
  474
+
  475
+The style you use is up to you.
  476
+
  477
+Note that if you use this technique -- passing objects rather than strings --
  478
+the view prefix (as explained in "The view prefix" above) will have no effect.

0 notes on commit 0b71ffa

Please sign in to comment.
Something went wrong with that request. Please try again.