Django source code parsing (5) URL Configuration

Source: Internet
Author: User
Document directory
  • Ii. 1. url Mode
  • Ii. Binary URL Splitter
  • Ii. matching results
  • Ii. url configuration process

Django source code parsing (1)

Django source code parsing (2) manage. py

Django source code parsing (3) Django development server, wsgi standard implementation

Django source code parsing (4) Middleware

Django source code parsing (5) URL Configuration

 

1. What is URL configuration?

URL configuration (urlconf) Is like the directory of the website supported by Django. It is essentially a ing table between the URL mode and the view functions to be called for this URL mode. In this way, you tell Django that this code is called for this URL and that code is called for that URL. For example, when a user accesses/Foo/, the view function foo_view () is called. This view function exists in the view. py file of the python module.

RunStartproject django-admin.pyThe script automatically creates a urlconf (that isURLs. pyFile ). In the settings. py file automatically created at the same time, create a variable root_urlconf whose value is the module name of the root urlconf. The default value is the module name of the URLs. py file.

For example, the root directory of my Django project is "Pearl", and the default value of root_urlconf is "Pearl. URLs ".

2. How does Django handle URL configuration?

To understand how Django handles URL configuration, we must first understand several concepts.

Ii. 1. url Mode

The URL mode refers to every value contained in the tuples named urlpatterns in the Django URLs module. The URL mode usually generates the content of the urlpatterns tuples by the patterns method.

You must specify the following content for each URL mode:

  1. A regular expression string.
  2. A callable object is usually a view function or a string that specifies the path of the view function.
  3. Optional default parameter (Dictionary form) to be passed to the view function ).
  4. An optional name parameter.
  5. Path prefix, which is added before the view function path string to form a complete view function path. You can specify the path by using the first parameter of the patterns method.

As you can see, those who know about Django may ask, "Isn't there a second URL mode using the include method? Dude, you didn't hold it. Wow ?" O (distinct _ distinct) O ~ I will try again later.

Class Django. Core. urlresolvers. regexurlpattern is used to represent the Django URL mode.

class RegexURLPattern(object):    def __init__(self, regex, callback, default_args=None, name=None):        # regex is a string representing a regular expression.        # callback is either a string like 'foo.views.news.stories.story_detail'        # which represents the path to a module and a view function name, or a        # callable object (view).        self.regex = re.compile(regex, re.UNICODE)        if callable(callback):            self._callback = callback        else:            self._callback = None            self._callback_str = callback        self.default_args = default_args or {}        self.name = name    def __repr__(self):        return '<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern)    def add_prefix(self, prefix):        """        Adds the prefix string to a string-based callback.        """        if not prefix or not hasattr(self, '_callback_str'):            return        self._callback_str = prefix + '.' + self._callback_str    def resolve(self, path):        match = self.regex.search(path)        if match:            # If there are any named groups, use those as kwargs, ignoring            # non-named groups. Otherwise, pass all non-named arguments as            # positional arguments.            kwargs = match.groupdict()            if kwargs:                args = ()            else:                args = match.groups()            # In both cases, pass any extra_kwargs as **kwargs.            kwargs.update(self.default_args)            return ResolverMatch(self.callback, args, kwargs, self.name)    def _get_callback(self):        if self._callback is not None:            return self._callback        try:            self._callback = get_callable(self._callback_str)        except ImportError, e:            mod_name, _ = get_mod_func(self._callback_str)            raise ViewDoesNotExist("Could not import %s. Error was: %s" % (mod_name, str(e)))        except AttributeError, e:            mod_name, func_name = get_mod_func(self._callback_str)            raise ViewDoesNotExist("Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)))        return self._callback    callback = property(_get_callback)

 

Ii. Binary URL Splitter

Generally, a URL parser corresponds to a URL configuration module. It can contain multiple URL modes or multiple other URL Resolvers. through this design of the Inclusion structure, Django can parse the URL hierarchy.

The URL parser is the key for Django to decouple apps from projects. Generally, the URL configuration module operated by the include method will be interpreted as a URL parser.

Each URL splitter must specify the following content:

  1. Whether the start part of a regular expression string matches a regular expression. For example, if a regular expression is matched, after the successful match is removed, the remaining part matches the URL pattern and URL parser.
  2. URL configuration module name or URL configuration module reference.
  3. Optional key parameters (Dictionary form ).
  4. Optional app name.
  5. Optional namespace name.

Class Django. Core. urlresolvers. regexurlresolver is used to represent the URL parser.

class RegexURLResolver(object):    def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None):        # regex is a string representing a regular expression.        # urlconf_name is a string representing the module containing URLconfs.        self.regex = re.compile(regex, re.UNICODE)        self.urlconf_name = urlconf_name        if not isinstance(urlconf_name, basestring):            self._urlconf_module = self.urlconf_name        self.callback = None        self.default_kwargs = default_kwargs or {}        self.namespace = namespace        self.app_name = app_name        self._reverse_dict = None        self._namespace_dict = None        self._app_dict = None    def __repr__(self):        return '<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern)    def _populate(self):        lookups = MultiValueDict()        namespaces = {}        apps = {}        for pattern in reversed(self.url_patterns):            p_pattern = pattern.regex.pattern            if p_pattern.startswith('^'):                p_pattern = p_pattern[1:]            if isinstance(pattern, RegexURLResolver):                if pattern.namespace:                    namespaces[pattern.namespace] = (p_pattern, pattern)                    if pattern.app_name:                        apps.setdefault(pattern.app_name, []).append(pattern.namespace)                else:                    parent = normalize(pattern.regex.pattern)                    for name in pattern.reverse_dict:                        for matches, pat in pattern.reverse_dict.getlist(name):                            new_matches = []                            for piece, p_args in parent:                                new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches])                            lookups.appendlist(name, (new_matches, p_pattern + pat))                    for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items():                        namespaces[namespace] = (p_pattern + prefix, sub_pattern)                    for app_name, namespace_list in pattern.app_dict.items():                        apps.setdefault(app_name, []).extend(namespace_list)            else:                bits = normalize(p_pattern)                lookups.appendlist(pattern.callback, (bits, p_pattern))                if pattern.name is not None:                    lookups.appendlist(pattern.name, (bits, p_pattern))        self._reverse_dict = lookups        self._namespace_dict = namespaces        self._app_dict = apps    def _get_reverse_dict(self):        if self._reverse_dict is None:            self._populate()        return self._reverse_dict    reverse_dict = property(_get_reverse_dict)    def _get_namespace_dict(self):        if self._namespace_dict is None:            self._populate()        return self._namespace_dict    namespace_dict = property(_get_namespace_dict)    def _get_app_dict(self):        if self._app_dict is None:            self._populate()        return self._app_dict    app_dict = property(_get_app_dict)    def resolve(self, path):        tried = []        match = self.regex.search(path)        if match:            new_path = path[match.end():]            for pattern in self.url_patterns:                try:                    sub_match = pattern.resolve(new_path)                except Resolver404, e:                    sub_tried = e.args[0].get('tried')                    if sub_tried is not None:                        tried.extend([[pattern] + t for t in sub_tried])                    else:                        tried.append([pattern])                else:                    if sub_match:                        sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])                        sub_match_dict.update(self.default_kwargs)                        for k, v in sub_match.kwargs.iteritems():                            sub_match_dict[smart_str(k)] = v                        return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [self.namespace] + sub_match.namespaces)                    tried.append([pattern])            raise Resolver404({'tried': tried, 'path': new_path})        raise Resolver404({'path' : path})    def _get_urlconf_module(self):        try:            return self._urlconf_module        except AttributeError:            self._urlconf_module = import_module(self.urlconf_name)            return self._urlconf_module    urlconf_module = property(_get_urlconf_module)    def _get_url_patterns(self):        patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)        try:            iter(patterns)        except TypeError:            raise ImproperlyConfigured("The included urlconf %s doesn't have any patterns in it" % self.urlconf_name)        return patterns    url_patterns = property(_get_url_patterns)    def _resolve_special(self, view_type):        callback = getattr(self.urlconf_module, 'handler%s' % view_type, None)        if not callback:            # No handler specified in file; use default            # Lazy import, since urls.defaults imports this file            from django.conf.urls import defaults            callback = getattr(defaults, 'handler%s' % view_type)        try:            return get_callable(callback), {}        except (ImportError, AttributeError), e:            raise ViewDoesNotExist("Tried %s. Error was: %s" % (callback, str(e)))    def resolve404(self):        return self._resolve_special('404')    def resolve500(self):        return self._resolve_special('500')    def reverse(self, lookup_view, *args, **kwargs):        if args and kwargs:            raise ValueError("Don't mix *args and **kwargs in call to reverse()!")        try:            lookup_view = get_callable(lookup_view, True)        except (ImportError, AttributeError), e:            raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))        possibilities = self.reverse_dict.getlist(lookup_view)        for possibility, pattern in possibilities:            for result, params in possibility:                if args:                    if len(args) != len(params):                        continue                    unicode_args = [force_unicode(val) for val in args]                    candidate =  result % dict(zip(params, unicode_args))                else:                    if set(kwargs.keys()) != set(params):                        continue                    unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()])                    candidate = result % unicode_kwargs                if re.search(u'^%s' % pattern, candidate, re.UNICODE):                    return candidate        # lookup_view can be URL label, or dotted path, or callable, Any of        # these can be passed in at the top, but callables are not friendly in        # error messages.        m = getattr(lookup_view, '__module__', None)        n = getattr(lookup_view, '__name__', None)        if m is not None and n is not None:            lookup_view_s = "%s.%s" % (m, n)        else:            lookup_view_s = lookup_view        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "                "arguments '%s' not found." % (lookup_view_s, args, kwargs))

 

Ii. matching results

The matching result is returned when the URL is correctly matched.

The following content must be specified for the matching result:

  1. A callable object, usually a view function.
  2. View function parameters. These parameters are usually the values matching the regular expression naming groups in URL mode.
  3. View function keyword parameters. These parameters are usually set in the URL method to be passed to the view function (in dictionary form ).
  4. Optional URL name parameters.
  5. Optional app name parameters.
  6. Optional namespace parameters.

The class Django. Core. urlresolvers. resolvermatch is used to indicate matching results. The resolvermatch class implements the _ getitem _ method, which can be used to obtain view function references and view function parameters, just like the metadata operation,

This allows you to call view functions.

class ResolverMatch(object):    def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=None):        self.func = func        self.args = args        self.kwargs = kwargs        self.app_name = app_name        if namespaces:            self.namespaces = [x for x in namespaces if x]        else:            self.namespaces = []        if not url_name:            if not hasattr(func, '__name__'):                # An instance of a callable class                url_name = '.'.join([func.__class__.__module__, func.__class__.__name__])            else:                # A function                url_name = '.'.join([func.__module__, func.__name__])        self.url_name = url_name    def namespace(self):        return ':'.join(self.namespaces)    namespace = property(namespace)    def view_name(self):        return ':'.join([ x for x in [ self.namespace, self.url_name ]  if x ])    view_name = property(view_name)    def __getitem__(self, index):        return (self.func, self.args, self.kwargs)[index]    def __repr__(self):        return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name='%s', app_name='%s', namespace='%s')" % (            self.func, self.args, self.kwargs, self.url_name, self.app_name, self.namespace)

 

Ii. url configuration process

Through understanding the URL pattern, URL parser, and URL matching results, we have basically understood the processing process of URL configuration.

Let's look at the code to start processing URL Configuration:

if hasattr(request, "urlconf"):    # Reset url resolver with a custom urlconf.    urlconf = request.urlconf    urlresolvers.set_urlconf(urlconf)    resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)callback, callback_args, callback_kwargs = resolver.resolve(        request.path_info)

Thanks to the hierarchical design of the URL parser, these lines of code complete the URL configuration process.

 

Insert: URL matching error page

When the URL is not correctly matched, Django usually reports a 404 error. If the development mode is used, an abnormal page is displayed, including many contents such as the URL module. How can this problem be achieved?

When the URL parser is used to parse the URL configuration, if there is no completely matched pattern, a resolver404 exception will be thrown.

The handling of resolver404 exceptions may be included in the exception middleware introduced in the previous article <Django source code parsing (4) middleware> (just a guess. If you are interested, you can study it .)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.