Django

Code

Changeset 2910

Show
Ignore:
Timestamp:
05/15/06 23:05:55 (2 years ago)
Author:
adrian
Message:

Added first stab at reverse matching to urlresolvers.py

Files:

Legend:

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

    r2809 r2910  
    1515    pass 
    1616 
     17class NoReverseMatch(Exception): 
     18    pass 
     19 
    1720def get_mod_func(callback): 
    1821    # Converts 'django.views.news.stories.story_detail' to 
     
    2023    dot = callback.rindex('.') 
    2124    return callback[:dot], callback[dot+1:] 
     25 
     26class MatchChecker(object): 
     27    "Class used in reverse RegexURLPattern lookup." 
     28    def __init__(self, args, kwargs): 
     29        self.args, self.kwargs = args, kwargs 
     30        self.current_arg = 0 
     31 
     32    def __call__(self, match_obj): 
     33        # match_obj.group(1) is the contents of the parenthesis. 
     34        # First we need to figure out whether it's a named or unnamed group. 
     35        # 
     36        grouped = match_obj.group(1) 
     37        m = re.search(r'^\?P<(\w+)>(.*?)$', grouped) 
     38        if m: # If this was a named group... 
     39            # m.group(1) is the name of the group 
     40            # m.group(2) is the regex. 
     41            try: 
     42                value = self.kwargs[m.group(1)] 
     43            except KeyError: 
     44                # It was a named group, but the arg was passed in as a 
     45                # positional arg or not at all. 
     46                try: 
     47                    value = self.args[self.current_arg] 
     48                    self.current_arg += 1 
     49                except IndexError: 
     50                    # The arg wasn't passed in. 
     51                    raise NoReverseMatch('Not enough positional arguments passed in') 
     52            test_regex = m.group(2) 
     53        else: # Otherwise, this was a positional (unnamed) group. 
     54            try: 
     55                value = self.args[self.current_arg] 
     56                self.current_arg += 1 
     57            except IndexError: 
     58                # The arg wasn't passed in. 
     59                raise NoReverseMatch('Not enough positional arguments passed in') 
     60            test_regex = grouped 
     61        # Note we're using re.match here on purpose because the start of 
     62        # to string needs to match. 
     63        if not re.match(test_regex + '$', str(value)): # TODO: Unicode? 
     64            raise NoReverseMatch("Value %r didn't match regular expression %r" % (value, test_regex)) 
     65        return str(value) # TODO: Unicode? 
    2266 
    2367class RegexURLPattern: 
     
    59103            raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)) 
    60104 
     105    def reverse(self, viewname, *args, **kwargs): 
     106        if viewname != self.callback: 
     107            raise NoReverseMatch 
     108        return self.reverse_helper(*args, **kwargs) 
     109 
     110    def reverse_helper(self, *args, **kwargs): 
     111        """ 
     112        Does a "reverse" lookup -- returns the URL for the given args/kwargs. 
     113        The args/kwargs are applied to the regular expression in this 
     114        RegexURLPattern. For example: 
     115 
     116            >>> RegexURLPattern('^places/(\d+)/$').reverse_helper(3) 
     117            'places/3/' 
     118            >>> RegexURLPattern('^places/(?P<id>\d+)/$').reverse_helper(id=3) 
     119            'places/3/' 
     120            >>> RegexURLPattern('^people/(?P<state>\w\w)/(\w+)/$').reverse_helper('adrian', state='il') 
     121            'people/il/adrian/' 
     122 
     123        Raises NoReverseMatch if the args/kwargs aren't valid for the RegexURLPattern. 
     124        """ 
     125        # TODO: Handle nested parenthesis in the following regex. 
     126        result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), self.regex.pattern) 
     127        return result.replace('^', '').replace('$', '') 
     128 
    61129class RegexURLResolver(object): 
    62130    def __init__(self, regex, urlconf_name): 
     
    65133        self.regex = re.compile(regex) 
    66134        self.urlconf_name = urlconf_name 
     135        self.callback = None 
    67136 
    68137    def resolve(self, path): 
     
    111180    def resolve500(self): 
    112181        return self._resolve_special('500') 
     182 
     183    def reverse(self, viewname, *args, **kwargs): 
     184        for pattern in self.urlconf_module.urlpatterns: 
     185            if pattern.callback == viewname: 
     186                try: 
     187                    return pattern.reverse_helper(*args, **kwargs) 
     188                except NoReverseMatch: 
     189                    continue 
     190        raise NoReverseMatch