Django2 路由分发Path源码解析

早期版本

早期版本路由配置都是用的url(),后面是path 和re_path,也可以使用url() 但是我们进入url后,会看到如下代码

def url(regex, view, kwargs=None, name=None):
    return re_path(regex, view, kwargs, name)

正题

当创建项目后,setting.py 中 会有一个配置url路由的总设置

ROOT_URLCONF = 'XXX.urls'  # xxx是你的项目名

'''附加小知识'''
# 如果要在别处调用
from django.utils.module_loading import import_string  # 把字符串转成 导入模块语句
# 等同于 from luff.. import urls
import_string(settings.ROOT_URLCONF)

解析

路由分为2种

  • 一种是根(没有再往下的分发了)URLPattern
  • 一种是节点(有分发)URLResolver
def _path(route, view, kwargs=None, name=None, Pattern=None):
     # 这里做了分支处理,如果是一个list或者tuple,就用URLResolver处理
     if isinstance(view, (list, tuple)):
         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)

URLPattern

# 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,定义如下:

# 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

# 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配置。

总结

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

参考

本文参考:https://www.jianshu.com/p/5b7bb930b170

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值