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)
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)
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 中的一个视图类,用于显示数据库模型中的单个对象的详细信息。该类继承自 SingleObjectMixin
、BaseDetailView
和 View
,位于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 中的一个通用视图类,用于显示一个对象列表。该类继承自 MultipleObjectMixin
、BaseListView
和 View
。
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">« 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 »</a>
{% endif %}
</span>
</div>
</body>
</html>
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,
])
# ...(省略部分代码)...
```
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).
"""
# ...(省略部分代码)...