flask中url的本质:

吧url和视图函数封装到一个Rule对象里面去了,并且吧这个对象添加到url_map中
Rule={"url":'/index','method':'index'}
url_map = [{"url":'/index','method':'index'},{"url":'/index','method':'index'}]

第一步:

app = Flask(__name__)

第二步:实例化一个对象,执行构造方法

if self.has_static_folder:
     self.add_url_rule(self.static_url_path + '/<path:filename>',
            endpoint='static',
            view_func=self.send_static_file)

第三步:

@setupmethod
    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):  
#吧url和视图函数保存到了一个Rule对象中,并且把这个对象添加到了url_map列表中
        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(rule)

第四步:

   url_rule_class = Rule

@implements_to_string
class Rule(RuleFactory):

    def __init__(self, string, defaults=None, subdomain=None, methods=None,
                 build_only=False, endpoint=None, strict_slashes=None,
                 redirect_to=None, alias=False, host=None):
        if not string.startswith('/'):
            raise ValueError('urls must start with a leading slash')
        self.rule = string
        self.is_leaf = not string.endswith('/')

        self.map = None
        self.strict_slashes = strict_slashes
        self.subdomain = subdomain
        self.host = host
        self.defaults = defaults
        self.build_only = build_only
        self.alias = alias
        if methods is None:
            self.methods = None
        else:
            if isinstance(methods, str):
                raise TypeError('param `methods` should be `Iterable[str]`, not `str`')
            self.methods = set([x.upper() for x in methods])
            if 'HEAD' not in self.methods and 'GET' in self.methods:
                self.methods.add('HEAD')
        self.endpoint = endpoint
        self.redirect_to = redirect_to

        if defaults:
            self.arguments = set(map(str, defaults))
        else:
            self.arguments = set()
        self._trace = self._converters = self._regex = self._argument_weights = None

    def empty(self):
        """
        Return an unbound copy of this rule.

        This can be useful if want to reuse an already bound URL for another
        map.  See ``get_empty_kwargs`` to override what keyword arguments are
        provided to the new copy.
        """
        return type(self)(self.rule, **self.get_empty_kwargs())

    def get_empty_kwargs(self):
        """
        Provides kwargs for instantiating empty copy with empty()

        Use this method to provide custom keyword arguments to the subclass of
        ``Rule`` when calling ``some_rule.empty()``.  Helpful when the subclass
        has custom keyword arguments that are needed at instantiation.

        Must return a ``dict`` that will be provided as kwargs to the new
        instance of ``Rule``, following the initial ``self.rule`` value which
        is always provided as the first, required positional argument.
        """
        defaults = None
        if self.defaults:
            defaults = dict(self.defaults)
        return dict(defaults=defaults, subdomain=self.subdomain,
                    methods=self.methods, build_only=self.build_only,
                    endpoint=self.endpoint, strict_slashes=self.strict_slashes,
                    redirect_to=self.redirect_to, alias=self.alias,
                    host=self.host)

    def get_rules(self, map):
        yield self

    def refresh(self):
        """Rebinds and refreshes the URL.  Call this if you modified the
        rule in place.

        :internal:
        """
        self.bind(self.map, rebind=True)

    def bind(self, map, rebind=False):
        """Bind the url to a map and create a regular expression based on
        the information from the rule itself and the defaults from the map.

        :internal:
        """
        if self.map is not None and not rebind:
            raise RuntimeError('url rule %r already bound to map %r' %
                               (self, self.map))
        self.map = map
        if self.strict_slashes is None:
            self.strict_slashes = map.strict_slashes
        if self.subdomain is None:
            self.subdomain = map.default_subdomain
        self.compile()

    def get_converter(self, variable_name, converter_name, args, kwargs):
        """Looks up the converter for the given parameter.

        .. versionadded:: 0.9
        """
        if converter_name not in self.map.converters:
            raise LookupError('the converter %r does not exist' % converter_name)
        return self.map.converters[converter_name](self.map, *args, **kwargs)

    def compile(self):
        """Compiles the regular expression and stores it."""
        assert self.map is not None, 'rule not bound'

        if self.map.host_matching:
            domain_rule = self.host or ''
        else:
            domain_rule = self.subdomain or ''

        self._trace = []
        self._converters = {}
        self._static_weights = []
        self._argument_weights = []
        regex_parts = []

        def _build_regex(rule):
            index = 0
            for converter, arguments, variable in parse_rule(rule):
                if converter is None:
                    regex_parts.append(re.escape(variable))
                    self._trace.append((False, variable))
                    for part in variable.split('/'):
                        if part:
                            self._static_weights.append((index, -len(part)))
                else:
                    if arguments:
                        c_args, c_kwargs = parse_converter_args(arguments)
                    else:
                        c_args = ()
                        c_kwargs = {}
                    convobj = self.get_converter(
                        variable, converter, c_args, c_kwargs)
                    regex_parts.append('(?P<%s>%s)' % (variable, convobj.regex))
                    self._converters[variable] = convobj
                    self._trace.append((True, variable))
                    self._argument_weights.append(convobj.weight)
                    self.arguments.add(str(variable))
                index = index + 1

        _build_regex(domain_rule)
        regex_parts.append('\\|')
        self._trace.append((False, '|'))
        _build_regex(self.is_leaf and self.rule or self.rule.rstrip('/'))
        if not self.is_leaf:
            self._trace.append((False, '/'))

        if self.build_only:
            return
        regex = r'^%s%s$' % (
            u''.join(regex_parts),
            (not self.is_leaf or not self.strict_slashes) and
            '(?<!/)(?P<__suffix__>/?)' or ''
        )
        self._regex = re.compile(regex, re.UNICODE)

    def match(self, path, method=None):
        """Check if the rule matches a given path. Path is a string in the
        form ``"subdomain|/path"`` and is assembled by the map.  If
        the map is doing host matching the subdomain part will be the host
        instead.

        If the rule matches a dict with the converted values is returned,
        otherwise the return value is `None`.

        :internal:
        """
        if not self.build_only:
            m = self._regex.search(path)
            if m is not None:
                groups = m.groupdict()
                # we have a folder like part of the url without a trailing
                # slash and strict slashes enabled. raise an exception that
                # tells the map to redirect to the same url but with a
                # trailing slash
                if self.strict_slashes and not self.is_leaf and \
                        not groups.pop('__suffix__') and \
                        (method is None or self.methods is None or
                         method in self.methods):
                    raise RequestSlash()
                # if we are not in strict slashes mode we have to remove
                # a __suffix__
                elif not self.strict_slashes:
                    del groups['__suffix__']

                result = {}
                for name, value in iteritems(groups):
                    try:
                        value = self._converters[name].to_python(value)
                    except ValidationError:
                        return
                    result[str(name)] = value
                if self.defaults:
                    result.update(self.defaults)

                if self.alias and self.map.redirect_defaults:
                    raise RequestAliasRedirect(result)

                return result

    def build(self, values, append_unknown=True):
        """Assembles the relative url for that rule and the subdomain.
        If building doesn't work for some reasons `None` is returned.

        :internal:
        """
        tmp = []
        add = tmp.append
        processed = set(self.arguments)
        for is_dynamic, data in self._trace:
            if is_dynamic:
                try:
                    add(self._converters[data].to_url(values[data]))
                except ValidationError:
                    return
                processed.add(data)
            else:
                add(url_quote(to_bytes(data, self.map.charset), safe='/:|+'))
        domain_part, url = (u''.join(tmp)).split(u'|', 1)

        if append_unknown:
            query_vars = MultiDict(values)
            for key in processed:
                if key in query_vars:
                    del query_vars[key]

            if query_vars:
                url += u'?' + url_encode(query_vars, charset=self.map.charset,
                                         sort=self.map.sort_parameters,
                                         key=self.map.sort_key)

        return domain_part, url

    def provides_defaults_for(self, rule):
        """Check if this rule has defaults for a given rule.

        :internal:
        """
        return not self.build_only and self.defaults and \
            self.endpoint == rule.endpoint and self != rule and \
            self.arguments == rule.arguments

    def suitable_for(self, values, method=None):
        """Check if the dict of values has enough data for url generation.

        :internal:
        """
        # if a method was given explicitly and that method is not supported
        # by this rule, this rule is not suitable.
        if method is not None and self.methods is not None \
           and method not in self.methods:
            return False

        defaults = self.defaults or ()

        # all arguments required must be either in the defaults dict or
        # the value dictionary otherwise it's not suitable
        for key in self.arguments:
            if key not in defaults and key not in values:
                return False

        # in case defaults are given we ensure taht either the value was
        # skipped or the value is the same as the default value.
        if defaults:
            for key, value in iteritems(defaults):
                if key in values and value != values[key]:
                    return False

        return True

    def match_compare_key(self):
       
        return bool(self.arguments), -len(self._static_weights), self._static_weights,\
            -len(self._argument_weights), self._argument_weights

    def build_compare_key(self):
        """The build compare key for sorting.

        :internal:
        """
        return self.alias and 1 or 0, -len(self.arguments), \
            -len(self.defaults or ())

    def __eq__(self, other):
        return self.__class__ is other.__class__ and \
            self._trace == other._trace

    __hash__ = None

    def __ne__(self, other):
        return not self.__eq__(other)

    def __str__(self):
        return self.rule

    @native_string_result
    def __repr__(self):
        if self.map is None:
            return u'<%s (unbound)>' % self.__class__.__name__
        tmp = []
        for is_dynamic, data in self._trace:
            if is_dynamic:
                tmp.append(u'<%s>' % data)
            else:
                tmp.append(data)
        return u'<%s %s%s -> %s>' % (
            self.__class__.__name__,
            repr((u''.join(tmp)).lstrip(u'|')).lstrip(u'u'),
            self.methods is not None
            and u' (%s)' % u', '.join(self.methods)
            or u'',
            self.endpoint
        )

 

 





posted on 2018-03-12 16:00  海燕。  阅读(727)  评论(0编辑  收藏  举报