Django源码分析-视图层源码解读(五)

发布时间:2024年01月23日

1.django中HTTP请求相关类解读

  • BaseHandler 类是 Django 中处理请求和响应的基础类,它定义了一系列方法来执行请求和处理响应。以下是其主要组件和功能的详细介绍:
    1.中间件管理:
    load_middleware()方法负责从 settings.MIDDLEWARE 配置中加载中间件。
    中间件类会被动态加载和初始化,它们的方法(process_view、process_template_response、process_exception)被添加到相应的中间件列表中。adapt_method_mode()方法用于确保中间件和处理程序方法具有一致的执行模式(同步或异步)。

    class BaseHandler:
        _view_middleware = None
        _template_response_middleware = None
        _exception_middleware = None
        _middleware_chain = None
        #  中间件加载和管理
        def load_middleware(self, is_async=False):
            self._view_middleware = []
            self._template_response_middleware = []
            self._exception_middleware = []
            get_response = self._get_response_async if is_async else self._get_response
            handler = convert_exception_to_response(get_response)
            handler_is_async = is_async
            # 负责从 settings.MIDDLEWARE 中加载中间件。
            for middleware_path in reversed(settings.MIDDLEWARE):
                middleware = import_string(middleware_path)
                middleware_can_sync = getattr(middleware, 'sync_capable', True)
                middleware_can_async = getattr(middleware, 'async_capable', False)
                if not middleware_can_sync and not middleware_can_async:
                    raise RuntimeError(
                        'Middleware %s must have at least one of '
                        'sync_capable/async_capable set to True.' % middleware_path
                    )
                elif not handler_is_async and middleware_can_sync:
                    middleware_is_async = False
                else:
                    middleware_is_async = middleware_can_async
                try:
                    # 通过调用 adapt_method_mode 方法,确保中间件和处理程序的执行方式一致。
                    adapted_handler = self.adapt_method_mode(
                        middleware_is_async, handler, handler_is_async,
                        debug=settings.DEBUG, name='middleware %s' % middleware_path,
                    )
                    mw_instance = middleware(adapted_handler)
                except MiddlewareNotUsed as exc:
                    if settings.DEBUG:
                        if str(exc):
                            logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                        else:
                            logger.debug('MiddlewareNotUsed: %r', middleware_path)
                    continue
                else:
                    handler = adapted_handler
    
                if mw_instance is None:
                    raise ImproperlyConfigured(
                        'Middleware factory %s returned None.' % middleware_path
                    )
    
                if hasattr(mw_instance, 'process_view'):
                    self._view_middleware.insert(
                        0,
                        self.adapt_method_mode(is_async, mw_instance.process_view),
                    )
                if hasattr(mw_instance, 'process_template_response'):
                    self._template_response_middleware.append(
                        self.adapt_method_mode(is_async, mw_instance.process_template_response),
                    )
                if hasattr(mw_instance, 'process_exception'):
                    self._exception_middleware.append(
                        self.adapt_method_mode(False, mw_instance.process_exception),
                    )
    
                handler = convert_exception_to_response(mw_instance)
                handler_is_async = middleware_is_async
    
            handler = self.adapt_method_mode(is_async, handler, handler_is_async)
    		#  构建 _middleware_chain,作为中间件链的顶部处理程序。
            self. _middleware_chain = handler
    	#adapt_method_mode 方法用于确保中间件和处理程序方法具有一致的执行模式(同步或异步)
        def adapt_method_mode(
            self, is_async, method, method_is_async=None, debug=False, name=None,
        ):
            if method_is_async is None:
                method_is_async = asyncio.iscoroutinefunction(method)
            if debug and not name:
                name = name or 'method %s()' % method.__qualname__
            if is_async:
                if not method_is_async:
                    if debug:
                        logger.debug('Synchronous %s adapted.', name)
                    return sync_to_async(method, thread_sensitive=True)
            elif method_is_async:
                if debug:
                    logger.debug('Asynchronous %s adapted.', name)
                return async_to_sync(method)
            return method
    
    

    2.响应处理和视图解析和执行:
    get_response()get_response_async()方法负责设置默认的 URL 解析器,并将请求通过中间件链传递。_get_response()_get_response_async()方法解析并调用视图,应用视图、异常和模板响应中间件,并处理整个请求/响应中间件过程。

     # get_response 方法用于处理同步的 HTTP 请求
     def get_response(self, request):
            """Return an HttpResponse object for the given HttpRequest."""
            # 设置默认的 URL 解析器为当前线程
            set_urlconf(settings.ROOT_URLCONF)
            # 对中间件链返回的响应进行处理,包括添加资源关闭器和记录响应日志。
        response = self._middleware_chain(request)
        response._resource_closers.append(request.close)
        if response.status_code >= 400:
            log_response(
                '%s: %s', response.reason_phrase, request.path,
                response=response,
                request=request,
            )
        return response
    
    async def get_response_async(self, request):
        # get_response 的异步版本,用于处理异步的 HTTP 请求
        set_urlconf(settings.ROOT_URLCONF)
        # 通过 await 将请求传递给异步中间件链 _middleware_chain
        response = await self._middleware_chain(request)
        # 添加资源关闭器和记录响应日志的方式与同步请求相似。
        response._resource_closers.append(request.close)
        if response.status_code >= 400:
            await sync_to_async(log_response, thread_sensitive=False)(
                '%s: %s', response.reason_phrase, request.path,
                response=response,
                request=request,
            )
        return response
    # 用于解析和调用视图,然后应用视图、异常和模板响应中间件。
    def _get_response(self, request):
        response = None
        callback, callback_args, callback_kwargs = self.resolve_request(request)
        # 遍历视图中间件链,应用每个中间件方法。
        for middleware_method in self._view_middleware:
            response = middleware_method(request, callback, callback_args, callback_kwargs)
            if response:
                break
    	# 如果视图返回 None,则调用处理异常的中间件。
        if response is None:
            wrapped_callback = self.make_view_atomic(callback)
            # 检查视图是否返回 None,如果是,则引发错误。
            if asyncio.iscoroutinefunction(wrapped_callback):
                wrapped_callback = async_to_sync(wrapped_callback)
            try:
                # 自定义视图函数处理
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise
    
        # Complain if the view returned None (a common error).
        self.check_response(response, callback)
    
        # 如果响应支持延迟渲染,应用模板响应中间件并渲染响应。
        if hasattr(response, 'render') and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                # Complain if the template response middleware returned None (a common error).
                self.check_response(
                    response,
                    middleware_method,
                    name='%s.process_template_response' % (
                        middleware_method.__self__.__class__.__name__,
                    )
                )
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise
    
        return response
    
    async def _get_response_async(self, request):
        """
         _get_response 的异步版本,用于处理异步视图。
        """
        response = None
        callback, callback_args, callback_kwargs = self.resolve_request(request)
    
        # Apply view middleware.
        for middleware_method in self._view_middleware:
            response = await middleware_method(request, callback, callback_args, callback_kwargs)
            if response:
                break
    
        if response is None:
            wrapped_callback = self.make_view_atomic(callback)
            # If it is a synchronous view, run it in a subthread
            if not asyncio.iscoroutinefunction(wrapped_callback):
                wrapped_callback = sync_to_async(wrapped_callback, thread_sensitive=True)
            try:
                response = await wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                response = await sync_to_async(
                    self.process_exception_by_middleware,
                    thread_sensitive=True,
                )(e, request)
                if response is None:
                    raise
    
        # Complain if the view returned None or an uncalled coroutine.
        self.check_response(response, callback)
    
        # If the response supports deferred rendering, apply template
        # response middleware and then render the response
        if hasattr(response, 'render') and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = await middleware_method(request, response)
                # Complain if the template response middleware returned None or
                # an uncalled coroutine.
                self.check_response(
                    response,
                    middleware_method,
                    name='%s.process_template_response' % (
                        middleware_method.__self__.__class__.__name__,
                    )
                )
            try:
                if asyncio.iscoroutinefunction(response.render):
                    response = await response.render()
                else:
                    response = await sync_to_async(response.render, thread_sensitive=True)()
            except Exception as e:
                response = await sync_to_async(
                    self.process_exception_by_middleware,
                    thread_sensitive=True,
                )(e, request)
                if response is None:
                    raise
    
        # Make sure the response is not a coroutine
        if asyncio.iscoroutine(response):
            raise RuntimeError('Response is still a coroutine.')
        return response
    
    
  • WSGIHandler 的类,该类继承自 base.BaseHandler,用于处理WSGI(Web Server Gateway Interface)请求。

    class WSGIHandler(base.BaseHandler):
        request_class = WSGIRequest
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.load_middleware()
    	# __call__ 方法是类的主要入口点
        def __call__(self, environ, start_response):
            # 发送请求开始的信号
            set_script_prefix(get_script_name(environ))
            signals.request_started.send(sender=self.__class__, environ=environ)
            # 创建了一个 WSGIRequest 请求对象
            request = self.request_class(environ)
            # 获取响应对象
            response = self.get_response(request)
    		# 将响应对象的 _handler_class 属性设置为当前类的引用。
            response._handler_class = self.__class__
    
            status = '%d %s' % (response.status_code, response.reason_phrase)
            #  准备响应的状态、头部等信息
            response_headers = [
                *response.items(),
                *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
            ]
            start_response(status, response_headers)
            # 如果响应中包含文件需要流式传输,通过 environ['wsgi.file_wrapper'] 对文件进行包装,确保响应文件被正确关闭。
            if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
                response.file_to_stream.close = response.close
            response = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size)
        return response
    
  • HttpRequest 类是 Django 框架中用于表示 HTTP 请求的类,它提供了对请求的各种信息的封装和方便的访问方式。

    class HttpRequest:
    def __init__(self):
        # 初始化方法
    
    def get_full_path(self):
        # 返回包含查询参数的完整路径
        pass
    
    def build_absolute_uri(self):
        # 构建并返回请求的绝对 URI
        pass
    
    def is_secure(self):
        # 返回请求是否通过安全的 HTTPS 协议进行
        pass
    
    def get_host(self):
        # 返回请求的主机名,包括端口号
        pass
    
    def get_port(self):
        # 返回请求的端口号
        pass
    
    def get_signed_cookie(self, key, default=None, salt='', max_age=None):
        # 获取已签名的 Cookie
        pass
    
    def is_ajax(self):
        # 返回请求是否为 Ajax 请求
        pass
    
    # 其他方法
    def __getitem__(self, key):
        # 获取请求头部信息
        pass
    
    def get(self, key, default=None):
        # 获取请求头部信息,可设置默认值
        pass
    
    def items(self):
        # 返回请求头部信息的键值对列表
        pass
    
    def keys(self):
        # 返回请求头部信息的键列表
        pass
    
    def values(self):
        # 返回请求头部信息的值列表
        pass
    
    def pop(self, key, default=None):
        # 弹出请求头部信息
        pass
    
    def update(self, other_dict):
        # 更新请求头部信息
        pass
    
    def set_signed_cookie(self, key, value, salt='', max_age=None):
        # 设置已签名的 Cookie
        pass
    
    def delete_cookie(self, key):
        # 删除指定的 Cookie
        pass
    
    def get_raw_uri(self):
        # 获取原始 URI
        pass
    
    def is_same_domain(self, other):
        # 检查请求是否与另一个请求属于相同的域
        pass
    
    
  • WSGIHandler 的类,该类继承自 HttpRequestl类,用于处理WSGI(Web Server Gateway Interface)请求。

    class WSGIRequest(HttpRequest):
        def __init__(self, environ):
            # 构造函数,接收 WSGI 环境变量字典
            script_name = get_script_name(environ)
            #  如果 PATH_INFO 为空(例如,访问没有尾随斜杠的 SCRIPT_NAME URL),则操作为 '/' 请求。
            path_info = get_path_info(environ) or '/'
            # 设置实例的属性
            self.environ = environ
            self.path_info = path_info
            #  处理路径,确保路径以斜杠开头,避免 http://test/something 和 http://test//something 不同
            self.path = '%s/%s' % (script_name.rstrip('/'),
                                   path_info.replace('/', '', 1))
            self.META = environ
            self.META['PATH_INFO'] = path_info
            self.META['SCRIPT_NAME'] = script_name
            self.method = environ['REQUEST_METHOD'].upper()
            #  设置 content_type、content_params 和 encoding
            self._set_content_type_params(environ)
            try:
                content_length = int(environ.get('CONTENT_LENGTH'))
            except (ValueError, TypeError):
                content_length = 0
            # 创建 LimitedStream 对象,用于处理输入流
            self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
             # 标志是否已开始读取数据
            self._read_started = False
            # 用于存储 URL 匹配信息的属性
            self.resolver_match = None
    
        def _get_scheme(self):
            # 获取 URL 方案(scheme)
            return self.environ.get('wsgi.url_scheme')
    
        @cached_property
        def GET(self):
            # 获取查询字符串参数,返回 QueryDict 对象
            raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
            return QueryDict(raw_query_string, encoding=self._encoding)
    
        def _get_post(self):
        # 获取请求的 POST 数据
            if not hasattr(self, '_post'):
                self._load_post_and_files()
            return self._post
    
        def _set_post(self, post):
        # 设置请求的 POST 数据
            self._post = post
    
        @cached_property
        def COOKIES(self):
        # 获取请求中的 Cookie,返回字典
            raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
            return parse_cookie(raw_cookie)
    
        @property
        def FILES(self):
         # 获取请求中上传的文件信息
            if not hasattr(self, '_files'):
                self._load_post_and_files()
            return self._files
    
        POST = property(_get_post, _set_post)
    

2.django中请求和视图函数映射过程追踪

1. URL路由配置
在Django项目中,URL路由配置通常在urls.py文件中完成。这个文件定义了URL与视图函数之间的映射关系。

# 例子:urls.py

from django.urls import path
from .views import my_view

urlpatterns = [
    path('my-url/', my_view, name='my_view'),
]

2. URL请求处理
当用户访问’/my-url/'时,Django将处理这个请求,并尝试匹配URL路由配置。

# 例子:django.core.handlers.base.py 中 BaseHandler 类的 _get_response方法

def _get_response(self, request):
	# 省略部分代码
	#  ...
    # 调用resolve_request方法
    callback, callback_args, callback_kwargs = self.resolve_request(request)
    #  ...
    return response
 
 # 该方法用于解析请求的路径,找到与之匹配的视图函数。
 def resolve_request(self, request):
       if hasattr(request, 'urlconf'):
           urlconf = request.urlconf
           set_urlconf(urlconf)
           resolver = get_resolver(urlconf)
       else:
       	 # 即调用 get_resolver() 获取一个默认的 resolver 对象。
           resolver = get_resolver()
       # *****使用获取到的 resolver 对象调用 resolve 方法,该方法返回一个 ResolverMatch 对象,包含了与请求路径匹配的视图函数的信息。
       resolver_match = resolver.resolve(request.path_info)
       request.resolver_match = resolver_match
       return resolver_match

3. resolve方法详解
resolve() 方法是 Django 路由系统中的关键方法,负责递归地匹配给定的路径(path)并返回与之匹配的 ResolverMatch 对象.

# 位于django.urls.resolvers.py 中 URLResolver类

def resolve(self, path):
    path = str(path)  # path may be a reverse_lazy object
    tried = []  # 用于记录尝试过的匹配
    match = self.pattern.match(path)  # 使用正则表达式匹配路径
    if match:
        new_path, args, kwargs = match
        for pattern in self.url_patterns:
            try:
                # 递归调用,核心操作
                sub_match = pattern.resolve(new_path)
            except Resolver404 as e:
                # 如果子模式未匹配成功,记录尝试过的匹配
                self._extend_tried(tried, pattern, e.args[0].get('tried'))
            else:
                if sub_match:
                    # 合并当前匹配的参数与子模式的参数
                    sub_match_dict = {**kwargs, **self.default_kwargs}
                    # 更新 sub_match_dict,使用子模式的关键字参数
                    sub_match_dict.update(sub_match.kwargs)
                    # 如果有命名组,忽略所有非命名组;否则,将所有非命名组作为位置参数传递
                    sub_match_args = sub_match.args
                    if not sub_match_dict:
                        sub_match_args = args + sub_match.args
                    current_route = '' if isinstance(pattern, URLPattern) else str(pattern.pattern)
                    self._extend_tried(tried, pattern, sub_match.tried)
                    # 返回 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,
                    )
                tried.append([pattern])
        # 如果没有任何子模式匹配成功,抛出 Resolver404 异常
        raise Resolver404({'tried': tried, 'path': new_path})
    # 如果路径没有匹配成功,抛出 Resolver404 异常
    raise Resolver404({'path': path})

3. path()和include()方法详解
在Django中,path()include() 是 URL 配置中常用的两个方法。它们通常在 urls.py 文件中使用,用于定义应用程序的 URL 映射规则。path() 方法用于定义基本的 URL 模式,而 include() 方法用于包含其他 URL 模式

path() 方法源码分析:

path() 方法定义在 django.urls 模块中,用于创建基于字符串的 URL 模式。它接受两个参数:
第一个参数是字符串,表示 URL 模式。
第二个参数是视图函数或视图类。

def _path(route, view, kwargs=None, name=None, Pattern=None):
	# route: 一个字符串,表示 URL 模式。
    # view: 视图函数、视图类或包含其他 URL 模式的元组
    # Pattern: 一个可选的类,用于创建 URL 模式的实例,默认为 RoutePattern。
    
    # 如果是元组(包含其他 URL 模式的情况),则创建一个 URLResolver 实例,表示包含其他 URL 模式
    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 实例,表示一个具体的 URL 模式。
    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().')

# 通过 functools.partial 创建了 path 函数,固定了 Pattern 参数为 RoutePattern,即使用 RoutePattern 类来创建 URL 模式。
path = partial(_path, Pattern=RoutePattern)


# 通过调用 path 函数,可以更方便地创建 URL 模式,而不必每次都传递 Pattern 参数。例如:
url_pattern = path('example/', some_view, name='example-url')
# 这将使用默认的 RoutePattern 创建一个 URL 模式,表示 'example/' 路径对应的视图是 some_view。

include() 方法源码分析:

include() 方法定义在 django.urls 模块中,用于包含其他 URL 模式。它接受一个参数:
一个字符串,表示要包含的 URL 模式。

def include(arg, namespace=None):
    app_name = None
    # 如果 arg 是一个元组,说明可能是包含其他 URL 模块的情况,提取其中的 urlconf_module 和 app_name。
    if isinstance(arg, tuple):
        # Callable returning a namespace hint.
        try:
            urlconf_module, app_name = arg
        except ValueError:
            if namespace:
                raise ImproperlyConfigured(
                    'Cannot override the namespace for a dynamic module that '
                    'provides a namespace.'
                )
            raise ImproperlyConfigured(
                'Passing a %d-tuple to include() is not supported. Pass a '
                '2-tuple containing the list of patterns and app_name, and '
                'provide the namespace argument to include() instead.' % len(arg)
            )
    else:
        # 如果 arg 不是元组,直接将其作为 urlconf_module。
        urlconf_module = arg

    if isinstance(urlconf_module, str):
    	# 使用 import_module 函数导入相应的模块。
        urlconf_module = import_module(urlconf_module)
    patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
    app_name = getattr(urlconf_module, 'app_name', app_name)
    if namespace and not app_name:
        raise ImproperlyConfigured(
            'Specifying a namespace in include() without providing an app_name '
            'is not supported. Set the app_name attribute in the included '
            'module, or pass a 2-tuple containing the list of patterns and '
            'app_name instead.',
        )
    namespace = namespace or app_name
    #确保模块的 urlpatterns 可以被迭代。
    if isinstance(patterns, (list, tuple)):
        for url_pattern in patterns:
            pattern = getattr(url_pattern, 'pattern', None)
            if isinstance(pattern, LocalePrefixPattern):
                raise ImproperlyConfigured(
                    'Using i18n_patterns in an included URLconf is not allowed.'
                )
    return (urlconf_module, app_name, namespace)

3.django中view类的源码解读

  • View 类:Django 的视图类是用于处理 HTTP 请求的一种方式,通常继承自基础视图类( View 类)。Django 中的 View 类定义在 django.views.generic.base 模块中。

    from django.http import HttpResponse
    from django.views import View
    
    class MyView(View):
        def get(self, request, *args, **kwargs):
            # 处理 GET 请求的逻辑
            return HttpResponse("Hello, World!")
    
    class View:
    	# as_view 方法是一个类方法,用于返回一个可调用的函数,这个函数实际上是视图处理请求的入口点。这个函数是闭包,保持了类初始化参数的状态。
    	@classmethod
    	def as_view(cls, **initkwargs):
    	    # ...(省略部分代码)
    	
    	    def view(request, *args, **kwargs):
    	        self = cls(**initkwargs)
    	        # setup 方法用于初始化视图类的一些共享属性
    	        self.setup(request, *args, **kwargs)
    	        if not hasattr(self, 'request'):
    	            raise AttributeError(
    	                "%s instance has no 'request' attribute. Did you override "
    	                "setup() and forget to call super()?" % cls.__name__
    	            )
    	        # dispatch 方法用于根据请求的 HTTP 方法选择调用相应的处理函数 
    	        return self.dispatch(request, *args, **kwargs)
    	
    	    # ...(省略部分代码)
    	
    	    return view
    		
    		# setup 方法用于初始化视图类的一些共享属性,包括 request、args 和 kwargs
    		def setup(self, request, *args, **kwargs):
    		    """
    		    Initialize attributes shared by all view methods.
    		    """
    		    self.request = request
    		    self.args = args
    		    self.kwargs = kwargs
    	    
    	    # dispatch 方法用于根据请求的 HTTP 方法选择调用相应的处理函数(例如,get、post)。	
    		def dispatch(self, request, *args, **kwargs):
    		    # ...(省略部分代码)
    		
    		    if request.method.lower() in self.http_method_names:
    		        # 调用对应的 HTTP 方法处理函数(例如,get、post)
    		        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    		    else:
    		        handler = self.http_method_not_allowed
    		
    		    # ...(省略部分代码)
    		
    		    return handler(request, *args, **kwargs)
    
    
  • TemplateView类:TemplateView类是 Django 中的一个视图类,通过继承 BaseTemplateView类、TemplateResponseMixin 类和 ContextMixin类实现了模板的渲染和上下文数据的传递。

    class TemplateResponseMixin:
        # ...(省略部分代码)
        def render_to_response(self, context, **response_kwargs):
            """
            该方法用于渲染模板并返回一个响应对象
            """
            # ...(省略部分代码)
    
            return self.response_class(
                request=self.request,
            template=self.get_template_names(), # 获取模板名称 
            context=context,
            **response_kwargs,
        )
    
    class ContextMixin:
        # ...(省略部分代码)
    
        def get_context_data(self, **kwargs):
            """
            该方法用于返回传递给模板的上下文数据。子类可以重写此方法以提供特定的上下文数据。
            """
        # ...(省略部分代码)
    
        return context
        
    class TemplateView(TemplateResponseMixin, ContextMixin, View):
    	 def get(self, request, *args, **kwargs):
    	    # get_context_data 方法,该方法来自 ContextMixin 类,用于获取模板渲染时的上下文数据
            context = self.get_context_data(**kwargs)
            # render_to_response 方法,该方法来自 TemplateResponseMixin 类,将上下文数据传递给模板并返回渲染后的响应对象。
            return self.render_to_response(context)
    

    简单案例:

    创建了一个名为 HomePageView 的视图类,它继承自 TemplateView。我们指定了要使用的模板名称为 ‘home.html’,并通过 get_context_data 方法向模板传递了一个变量 ‘variable’。

    # views.py
    from django.views.generic import TemplateView
    
    class HomePageView(TemplateView):
        template_name = 'home.html'
    
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            context['variable'] = 'Hello, TemplateView!'
            return context
    
    

    我们需要在 urls.py 中将这个视图与一个 URL 关联起来:

    # urls.py
    from django.urls import path
    from .views import HomePageView
    
    urlpatterns = [
        path('', HomePageView.as_view(), name='home'),
    ]
    

    这样,当用户访问根路径时,HomePageView 视图将会被调用,使用 ‘home.html’ 模板进行渲染。在模板中,我们可以使用传递的上下文数据,例如 {{ variable }},来显示 ‘Hello, TemplateView!’。

    <!-- templates/home.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <title>Home Page</title>
    </head>
    <body>
        <h1>{{ variable }}</h1>
    </body>
    </html>
    
    
  • DetailView类:DetailView 类是 Django 中的一个视图类,用于显示数据库模型中的单个对象的详细信息。该类继承自 SingleObjectMixinBaseDetailViewView,位于django/views/generic/detail.py文件。

    class SingleObjectMixin:
    # ...(省略部分代码)
    
        def get_object(self, queryset=None):
            """
            该方法用于获取要显示的对象。子类可以根据需要重写此方法以自定义对象的获取逻辑。
            """
            # ...(省略部分代码)
    
    class BaseDetailView(SingleObjectMixin, View):
    # ...(省略部分代码)
    	 # get 方法,表示处理 HTTP GET 请求的逻辑
        def get(self, request, *args, **kwargs):
            # ...(省略部分代码)
    		# 获取上下文数据
            context = self.get_context_data(object=self.object)
            # 渲染模板并返回响应对象。
            return self.render_to_response(context)
    
    
    class DetailView(SingleObjectMixin, BaseDetailView, View):
        """
        Render a "detail" view of an object.
        By default this is a model instance looked up from `self.queryset`, but the
        view will support display of *any* object by overriding `self.get_object()`.
        """
        pass
    
    

    简单案例:

    我们定义了一个简单的 Book 模型,并创建了一个 BookDetailView 类,该类继承自 DetailView。

    # models.py
    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        author = models.CharField(max_length=50)
        description = models.TextField()
    
    # views.py
    from django.views.generic import DetailView
    from .models import Book
    
    class BookDetailView(DetailView):
        model = Book  # 表示该视图显示的对象是 Book 模型的实例。
        template_name = 'book_detail.html'  # 表示要使用的模板名称
        context_object_name = 'book' #  表示传递给模板的上下文数据中的变量名
    
    

    接下来,我们需要在 urls.py 中将这个视图与一个 URL 关联起来:

    # urls.py
    from django.urls import path
    from .views import BookDetailView
    
    # 我们使用了 <int:pk> 部分来捕获 URL 中的整数参数,并传递给 DetailView 中的 pk 属性,用于查找要显示的 Book 对象。
    urlpatterns = [
        path('book/<int:pk>/', BookDetailView.as_view(), name='book-detail'),
    ]
    
    

    最后,我们创建一个模板文件 ‘book_detail.html’ 来显示书籍详细信息,这个模板中使用了 {{ book.title }}、{{ book.author }} 和 {{ book.description }} 来显示书籍的详细信息。

    <!-- templates/book_detail.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <title>{{ book.title }}</title>
    </head>
    <body>
        <h1>{{ book.title }}</h1>
        <p>Author: {{ book.author }}</p>
        <p>Description: {{ book.description }}</p>
    </body>
    </html>
    
    
  • ListView类:ListView 是 Django 中的一个通用视图类,用于显示一个对象列表。该类继承自 MultipleObjectMixinBaseListViewView

    class MultipleObjectMixin:
        # ...(省略部分代码)
    	    def get(self, request, *args, **kwargs):
    	        # ...(省略部分代码)
    	
    	        context = self.get_context_data()
    	        return self.render_to_response(context)
    
    	class BaseListView(MultipleObjectMixin, View):
    	   # ...(省略部分代码)
    		# get 方法,表示处理 HTTP GET 请求的逻辑
    	    def get(self, request, *args, **kwargs):
    	        # ...(省略部分代码)
    			# 获取上下文数据
    	        context = self.get_context_data()
    	        # 渲染模板并返回响应对象
    	        return self.render_to_response(context)
    	
    	class ListView(MultipleObjectMixin, BaseListView, View):
    	    """
    	    Render some list of objects.
    	    By default this is a model's queryset but it could be any list of items.
    	    """
    	   	 pass
    

    简单案例
    我们定义了一个简单的 Book 模型,并创建了一个 BookListView 类,该类继承自 ListView。

    # models.py
    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        author = models.CharField(max_length=50)
        description = models.TextField()
    
    # views.py
    from django.views.generic import ListView
    from .models import Book
    
    class BookListView(ListView):
        model = Book  # 表示该视图显示的对象列表是 Book 模型的实例。
        template_name = 'book_list.html'  # 表示要使用的模板名称
        context_object_name = 'books' # 表示传递给模板的上下文数据中的变量名
        paginate_by = 10 # 表示每页显示的对象数量,这里设置为 10,启用分页
    
    

    接下来,我们需要在 urls.py 中将这个视图与一个 URL 关联起来:

    # urls.py
    from django.urls import path
    from .views import BookListView
    
    urlpatterns = [
        path('books/', BookListView.as_view(), name='book-list'),
    ]
    

    最后,我们创建一个模板文件 ‘book_list.html’ 来显示书籍列表,这个模板中使用了 {% for book in books %} 来迭代书籍列表,并显示每本书的标题和作者。同时,也使用了分页的功能,通过 {% if books.has_previous %}、{% if books.has_next %} 等条件判断来显示分页链接。

    <!-- templates/book_list.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <title>Book List</title>
    </head>
    <body>
        <h1>Book List</h1>
        <ul>
            {% for book in books %}
                <li>{{ book.title }} - {{ book.author }}</li>
            {% endfor %}
        </ul>
    
        <div class="pagination">
            <span class="step-links">
                {% if books.has_previous %}
                    <a href="?page=1">&laquo; first</a>
                    <a href="?page={{ books.previous_page_number }}">previous</a>
                {% endif %}
    
                <span class="current">
                    Page {{ books.number }} of {{ books.paginator.num_pages }}.
                </span>
    
                {% if books.has_next %}
                    <a href="?page={{ books.next_page_number }}">next</a>
                    <a href="?page={{ books.paginator.num_pages }}">last &raquo;</a>
                {% endif %}
            </span>
        </div>
    </body>
    </html>
    
    

4.django中Cookies和Sessions模块源码解读

  • Cookies 模块源码解读
    Django 中的 Cookies 主要通过 django.http.HttpResponse 类来处理。

    # django/http/response.py
    
    class HttpResponseBase:
        # ... 省略部分代码 ...
    
        def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False, samesite=None):
            # ... 省略部分代码 ...
    
        def delete_cookie(self, key, path='/', domain=None):
            # ... 省略部分代码 ...
    
    
  • Sessions 模块源码解读
    Django 中的 Sessions 模块主要通过 django.contrib.sessions 应用来实现。SessionStore 类是 Django 中用于会话存储的一个关键类,它实现了会话的加载和保存等功能。

    # django/contrib/sessions/backends/db.py
    
    class SessionStore(BaseSession):
        # ...(省略部分代码)...
    	 # load 方法用于从数据库加载会话数据
       def load(self):
    	    """
    	    Load the session data from the database.
    	    """
    	    # ...(省略部分代码)...
    	
    	    session_data = self.decode(force_bytes(session_data))
    	    return session_data
    		
        # save 方法用于将会话数据保存到数据库
        def save(self, must_create=False):
    	    # ...(省略部分代码)...
    	
    	    data = self.encode(self._get_session(no_load=must_create))
    	    # ...(省略部分代码)...
    	
    	    if must_create:
    	        # ...(省略部分代码)...
    	        cursor.execute("INSERT INTO %s (session_key, session_data, expire_date) VALUES (?, ?, ?)" % self._meta.db_table, [
    	            self.session_key, data, self.expire_date,
    	        ])
    	    else:
    	        # ...(省略部分代码)...
    	        cursor.execute("UPDATE %s SET session_data = ?, expire_date = ? WHERE session_key = ?" % self._meta.db_table, [
    	            data, self.expire_date, self.session_key,
    	        ])
    	    # ...(省略部分代码)...
    
    
    	```
    
    

5.django中处理和上传文件源码解读

Django 中处理和上传文件的主要组件包括 django.core.files 模块和 django.core.files.uploadedfile 模块。

  • django.core.files 模块
    django.core.files 模块提供了与文件相关的一些基本类和函数。其中,File 类是一个基类,用于处理文件的读取、写入等操作。

    # django/core/files/base.py
    
    class File:
        # ...(省略部分代码)...
    
        def write(self, content):
            """
            write 方法用于将内容写入文件
            """
            # ...(省略部分代码)...
    
    
  • django.core.files.uploadedfile 模块
    django.core.files.uploadedfile 模块提供了处理上传文件的相关类。其中,UploadedFile 类是一个表示上传文件的类。它包含了一些用于处理上传文件的方法,如 chunks 方法用于按照指定的块大小读取文件内容。

    # django/core/files/uploadedfile.py
    
    class UploadedFile(File):
        # ...(省略部分代码)...
    	# chunks 方法用于按照指定的块大小读取文件内容
        def chunks(self, chunk_size=None):
            """
            Read the file and yield chunks of ``chunk_size`` bytes (defaults to
            settings.FILE_UPLOAD_MAX_MEMORY_SIZE).
            """
            # ...(省略部分代码)...
    
    
文章来源:https://blog.csdn.net/qq_52696823/article/details/135700365
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。