深入原理64式:9 django路由分发原理分析

mac2025-12-12  9

1 django的url路由分发原理 作用:解析请求的url,匹配找到对应的view函数来处理。

2 分析 2.1  项目默认会带有setting文件,它会有ROOT_URLCONF,而ROOT_URLCONF会指向一个默认的url配置文件。 样例:

urlpatterns = [     path('admin/', admin.site.urls),     path('blog/', views.page), ]

2.2 源码 # django/urls/conf.py

def _path(route, view, kwargs=None, name=None, Pattern=None):      # 这里做了分支处理,如果是一个list或者tuple,就用URLResolver处理      if isinstance(view, (list, tuple)):          # For include(...) processing.          pattern = Pattern(route, is_endpoint=False)          urlconf_module, app_name, namespace = view          return URLResolver(              pattern,              urlconf_module,              kwargs,              app_name=app_name,              namespace=namespace,          )      # 如果是一个正常的可调用的view函数,则用URLPattern处理      elif callable(view):          pattern = Pattern(route, name=name, is_endpoint=True)          return URLPattern(pattern, view, kwargs, name)      else:          raise TypeError('view must be a callable or a list/tuple in the case of include().')

 path = partial(_path, Pattern=RoutePattern)

分析: 当是一个正常的可调用的view函数的时候,就会当成URLPattern处理;但是如果是一个list或者tuple,则会当成URLResolver继续处理。那么继续看下URLPattern和URLResolver

2.3  # django/urls/resolvers.py

class URLPattern:     def __init__(self, pattern, callback, default_args=None, name=None):         # 需要匹配的urlpattern         self.pattern = pattern         # 对应的回调函数         self.callback = callback  # the view         # 参数等         self.default_args = default_args or {}         self.name = name

    def __repr__(self):         return '<%s %s>' % (self.__class__.__name__, self.pattern.describe())

    ...

    def resolve(self, path):         match = self.pattern.match(path)         if match:             # 如果匹配成功,则返回一个ResolverMatch             new_path, args, kwargs = match             # Pass any extra_kwargs as **kwargs.             kwargs.update(self.default_args)             return ResolverMatch(self.callback, args, kwargs, self.pattern.name)

分析: URLPattern包括根据正则表达式字符串生成的pattern对象,一个可调用的对象,以及一些参数。所以很明显,对应的就是url配置文件里面配置了可调用view函数或者对象的那些记录。这里面当匹配成功后,会返回ResolverMatch,定义如下:

2.4 # django/urls/resolvers.py

class ResolverMatch:     def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None):         self.func = func         self.args = args         self.kwargs = kwargs         self.url_name = url_name

        # If a URLRegexResolver doesn't have a namespace or app_name, it passes         # in an empty value.         self.app_names = [x for x in app_names if x] if app_names else []         self.app_name = ':'.join(self.app_names)         self.namespaces = [x for x in namespaces if x] if namespaces else []         self.namespace = ':'.join(self.namespaces)

        if not hasattr(func, '__name__'):             # A class-based view             self._func_path = func.__class__.__module__ + '.' + func.__class__.__name__         else:             # A function-based view             self._func_path = func.__module__ + '.' + func.__name__

        view_path = url_name or self._func_path         self.view_name = ':'.join(self.namespaces + [view_path])

    # 可以获取回调函数,或者参数     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_names=%s, namespaces=%s)" % (             self._func_path, self.args, self.kwargs, self.url_name,             self.app_names, self.namespaces,         )          分析: ResolverMatch就是匹配成功后返回的结果。它包含了可调用对象。通常是视图函数;参数等信息。以及因为实现了__getitem__方法,所以可以很方便的回调函数参数等信息。 最后还剩下URLResolver

2.5 # django/urls/resolvers.py class URLResolver:     ...     def resolve(self, path):         path = str(path)  # path may be a reverse_lazy object         tried = []         # 匹配path         match = self.pattern.match(path)         if match:             new_path, args, kwargs = match             # 如果匹配成功,则继续匹配它的url_patterns             for pattern in self.url_patterns:                 try:                     # 这里比较关键,这个pattern可能是urlpattern,也可能是URLResolver;如果是urlpattern,匹配成功则返回ResolverMatch;如果是URLResolver,则会递归调用下去。                     sub_match = pattern.resolve(new_path)                 except Resolver404 as 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:                         # 匹配成功,生成ResolverMatch返回。                         # Merge captured arguments in match with submatch                         sub_match_dict = {**kwargs, **self.default_kwargs}                         # Update the sub_match_dict with the kwargs from the sub_match.                         sub_match_dict.update(sub_match.kwargs)                         # If there are *any* named groups, ignore all non-named groups.                         # Otherwise, pass all non-named arguments as positional arguments.                         sub_match_args = sub_match.args                         if not sub_match_dict:                             sub_match_args = args + sub_match.args                         return ResolverMatch(                             sub_match.func,                             sub_match_args,                             sub_match_dict,                             sub_match.url_name,                             [self.app_name] + sub_match.app_names,                             [self.namespace] + sub_match.namespaces,                         )                     tried.append([pattern])             raise Resolver404({'tried': tried, 'path': new_path})         raise Resolver404({'path': path})         ...          分析: 最核心的就是看resolve方法。这里面在遍历url_patterns的时候,这里面的pattern可能是urlpattern,也可能是URLResolver;如果是urlpattern,匹配成功则返回ResolverMatch;如果是URLResolver,则会递归调用下去。所以这就可以解释为什么可以进行多级的url配置。

3 总结 ResolverMatch就是匹配结果,包含匹配成功后需要的信息; URLPattern是一条url映射信息的对象,包含了url映射对应的可调用对象等信息; URLResolver是实现url路由,解析url的关键的地方,它的url_patterns既可以是URLPattern也可以是URLResolver。正是因为这种设计, 实现了对URL的层级解析。

参考: https://blog.csdn.net/bbwangj/article/details/79935500

最新回复(0)