1 Django How to handle a request
When a user requests Django A page of the site , Here is Django The system decides which Python The algorithm the code uses :
Django Determine the use of root URLconf modular . Usually , This is a
ROOT_URLCONF
Set the value of the ( namely settings MediumROOT_URLCONF
), But if it comes in HttpRequest The object owns urlconf attribute ( Through middleware settings ), Its value will be used instead of ROOT_URLCONF Set up . Can be indjango/core/handlers/base.py
Find the logic .class BaseHandler: ... def _get_response(self, request): ... if hasattr(request, 'urlconf'): urlconf = request.urlconf set_urlconf(urlconf) resolver = get_resolver(urlconf) else: resolver = get_resolver()
- Django Load the Python Modules and look for available urlpatterns . It is django.urls.path() and ( or ) django.urls.re_path() The sequence of instances (sequence). It's actually what we wrote
url.py
- Django We're going to traverse each one in order URL Pattern , And then in the requested URL Stop after matching to the first pattern , And with path_info matching . This is the key to route matching , The relevant logic is in
django/urls/resolvers.py
. There are several important concepts , Such asRegexPattern
、RoutePattern
、URLPattern
、URLResolver
. amongURLResolver
There's nested logic , Detailed below . - Once you have URL The match is successful ,Django Import and call related views , This view is a Python function ( Or class based views class-based view ). If the match is successful, a
ResolverMatch
object . - without URL Matched , Or there is an exception in the matching process ,Django An appropriate error handling view is called .
This article details 2、3, namely urlpatterns Related concepts and route matching process .
2 URL The configuration file
stay Django 2 After that, we usually use path/re_path To set the route , There is also a special method include .
- path: For normal paths
- re_path: For regular paths
- include: I'll give you one url Profile import
The following example :
urlpatterns = [
path('index/', views.index), # Normal path
re_path(r'^articles/([0-9]{4})/$', views.articles), # Regular path
path("app01/", include("app01.urls")),
]
Profile above , Set up 3 strip urlpattern
, They are the common paths index/
And The view function views.index
, Regular path ^articles/([0-9]{4})/$
And view functions views.articles
binding .app01/
and app01.urls
binding ,app01.urls
Not a view function , It's a sub module urlpatterns.
You can see urlpattern
You can put a url
Bind to view function , Or with a son urlpattern
Binding .
2.1 path、re_path
Several functions for setting the route are defined in django/urls/conf.py
in .
def include(arg, namespace=None):
...
return (urlconf_module, app_name, namespace)
def _path(route, view, kwargs=None, name=None, Pattern=None):
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,
)
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)
re_path = partial(_path, Pattern=RegexPattern)
First of all, let's look at path and re_path, These two functions are called functools Below partial It encapsulates .partial Simply put, the function is to fix some parameters of a function , Returns a new function . Detailed documentation is available partial file .
It's not hard to understand path and re_path, They just bind different Pattern Parametric _path function . Further inspection _path Internal logic ,
- The first branch If the binding is a list perhaps tuple, Use URLResolver Parse , In fact, this is the time to use include To define urlpattern.
- In another case, if it's bound view Is callable , Use it URLPattern Parse .URLPattern Medium pattern Parameters are based on the adoption of path/re_path The methods correspond to RoutePattern/RegexPattern.
2.2 include
def include(arg, namespace=None):
...
if isinstance(urlconf_module, str):
urlconf_module = import_module(urlconf_module)
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
app_name = getattr(urlconf_module, 'app_name', app_name)
...
return (urlconf_module, app_name, namespace)
include The way to do it is through import_module To be defined url Module import . Return to an argument urlconf modular 、app_name、 Namespace namespace A tuple of components . Back to the one just above _path The first branch of . Substitute the parameters in this tuple into URLResolver
And back to .
3 URLPattern And URLResolver
3.1 URLPattern
As mentioned above, if url In the definition, the binding is one that can be called directly view. That's using URLPattern Go straight to the analysis .
class URLPattern:
def __init__(self, pattern, callback, default_args=None, name=None):
# Need to match urlpattern, This is based on path still re_path Namely RoutePattern or RegexPattern Example
self.pattern = pattern
self.callback = callback # the view
self.default_args = default_args or {}
self.name = name
...
def resolve(self, path):
call RoutePattern or RegexPattern In the example of match Method to match ( Note that this is not re In the module match)
match = self.pattern.match(path)
if match:
new_path, args, kwargs = match
# Pass any extra_kwargs as **kwargs.
kwargs.update(self.default_args)
# Match successfully returns `ResolverMatch`
return ResolverMatch(self.callback, args, kwargs, self.pattern.name, route=str(self.pattern))
...
URLPattern During initialization, one of the pattern It's based on the use of path/re_path They correspond to each other RoutePattern or RegexPattern. In fact, it is to specify whether the matching pattern is normal route or regular route .
3.2 URLResolver
URLResolver The core of the source code is resolve function , Is to introduce a path, Match .
class URLResolver:
def resolve(self, path):
path = str(path) # path may be a reverse_lazy object
tried = []
# matching path
match = self.pattern.match(path)
if match:
new_path, args, kwargs = match
# If the match is successful , Then continue to match its url_patterns
for pattern in self.url_patterns:
try:
# This pattern May be URLPattern, It could be URLResolver; If it is URLPattern, If the match is successful, return ResolverMatch; If it is URLResolver, Will be called recursively .
sub_match = pattern.resolve(new_path)
...
else:
if sub_match:
...
# Match successfully returns ResolverMatch
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,
self._join_route(current_route, sub_match.route),
)
tried.append([pattern])
raise Resolver404({'tried': tried, 'path': new_path})
raise Resolver404({'path': path})
URLResolver The key logic is Loop match pattern The process , If pattern yes URLPattern If the match is successful, return to ResolverMatch, If it's another one URLResolver, The recursive call is implemented .
Django Through this URLResolver Multi level URL To configure .
4 summary
Django There are several core concepts of routing matching path/re_path/include、RegexPattern/RoutePattern、URLPattern/URLResolver.
First use partial encapsulation _path, Bind a pattern Matching mode (RegexPattern/RoutePattern), This is used many times later pattern. And then it's based on view Tuple or callable view function , Separate use URLResolver and URLPattern Parse , Both classes will be returned to ResolverMatch, It calls back the result after successful matching (view and args etc. ).
In this paper, from the overall point of view, it is generally explained that Django Routing matching process , Some of the key points will be explained in detail .