Django

Code

Changeset 3554

Show
Ignore:
Timestamp:
08/11/06 02:01:29 (2 years ago)
Author:
adrian
Message:

Improved urlresolvers so that URLconfs can be passed objects instead of strings

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/core/urlresolvers.py

    r3506 r3554  
    8787    def __init__(self, regex, callback, default_args=None): 
    8888        # 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). 
    9192        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 
    9398        self.default_args = default_args or {} 
    9499 
     
    107112            kwargs.update(self.default_args) 
    108113 
    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  
    115     def get_callback(self): 
    116         mod_name, func_name = get_mod_func(self.callback) 
    117         try: 
    118             return getattr(__import__(mod_name, '', '', ['']), func_name) 
     114            return self.callback, args, kwargs 
     115 
     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) 
     120        try: 
     121            self._callback = getattr(__import__(mod_name, '', '', ['']), func_name) 
    119122        except ImportError, e: 
    120123            raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e)) 
    121124        except AttributeError, e: 
    122125            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) 
    123128 
    124129    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: 
    126136            raise NoReverseMatch 
    127137        return self.reverse_helper(*args, **kwargs) 
     
    186196        return self._resolve_special('500') 
    187197 
    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 
    189205        for pattern in self.urlconf_module.urlpatterns: 
    190206            if isinstance(pattern, RegexURLResolver): 
    191207                try: 
    192                     return pattern.reverse_helper(viewname, *args, **kwargs) 
     208                    return pattern.reverse_helper(lookup_view, *args, **kwargs) 
    193209                except NoReverseMatch: 
    194210                    continue 
    195             elif pattern.callback == viewname
     211            elif pattern.callback == lookup_view
    196212                try: 
    197213                    return pattern.reverse_helper(*args, **kwargs) 
     
    200216        raise NoReverseMatch 
    201217 
    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) 
    204220        result = reverse_helper(self.regex, *args, **kwargs) 
    205221        return result + sub_match 
  • django/trunk/docs/url_dispatch.txt

    r3506 r3554  
    432432as valid. For this reason, this technique is only useful if you're certain that 
    433433every view in the the included URLconf accepts the extra options you're passing. 
     434 
     435Passing callable objects instead of strings 
     436=========================================== 
     437 
     438**New in the Django development version.** 
     439 
     440Some developers find it more natural to pass the actual Python function object 
     441rather than a string containing the path to its module. This alternative is 
     442supported -- you can pass any callable object as the view. 
     443 
     444For 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 
     452You can accomplish the same thing by passing objects rather than strings. Just 
     453be 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 
     463The following example is functionally identical. It's just a bit more compact 
     464because it imports the module that contains the views, rather than importing 
     465each 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 
     475The style you use is up to you. 
     476 
     477Note that if you use this technique -- passing objects rather than strings -- 
     478the view prefix (as explained in "The view prefix" above) will have no effect.