【4.0】DRF之Request类源码分析
【一】引入
class BooksView(APIView):
def post(self, request):
'''
:param request: 新的request,不是原来的那个
:return:
'''
print(type(request)) # rest_framework中的新 request
# 继承 APIView 后,无论是post请求还是 get请求,获取数据都是从 data 中获取
print(request.data) # 新的request中有了data 属性
# 原来的request.POST中还有数据,但是数据类型支持的没有data全,比如json格式的数据
print(request.POST)
# 提交文件是 form-data 格式,还在 request.FILES 中
print(request.FILES)
# 新的request的其他属性,使用起来和原来的一样
print(request.method)
print(request.body) # 有文件传入时,用 data 属性就会报错
print(request.path)
# Response 和 JsonResponse 很像,但是比 JsonResponse 功能更加强大
# 可以传字符串,列表,字典都可以,就是不能传对象
return Response("ok")
-
总结
-
新的request有个data属性,以后只要是在body体中的数据
- 无论什么编码格式
- 无论什么请求方式
-
取文件数据还是从 FILES 中取
-
取其他的属性(method/request/path...),和原来的一样
- 原理是:新的Request重写的
__getattr__
方法,通过反射取到原来的request中的属性
- 原理是:新的Request重写的
-
request.GET
现在可以使用request.query_params
@property def query_params(self): return self._request.GET
-
【二】源码分析
【1】新的request分析
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
# 将原来的 request 重命名隐藏为新的 _request
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty
if self.parser_context is None:
self.parser_context = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,)
- 原来的request现在变成了 新的request._request
【2】Request 源码中的 __getattr__
方法
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
__getattr__
方法被定义为先尝试从一个名为_request
的属性中获取指定的属性(attr
)- 如果
_request
中存在该属性,则返回其对应的值。 - 如果
_request
中不存在该属性,就会触发AttributeError
异常。
- 如果
- 如果发生
AttributeError
异常- 代码将会调用
self.__getattribute__(attr)
来处理。 __getattribute__
是另一个特殊方法,用于获取对象的属性。- 通过调用
self.__getattribute__(attr)
,实际上是直接访问对象本身的属性,而不是从_request
中获取属性。
- 代码将会调用
- 这种处理方式是为了避免在
_request
属性中出现的任何属性错误导致无限递归的情况。- 因为在
__getattribute__
方法内部访问属性时 - 如果再次调用
__getattr__
方法,就会导致无限循环。
- 因为在
下面是示例代码的详解:
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
-
参数:
self
:对象本身。attr
:要访问的属性名。
-
处理逻辑:
- 首先,尝试从
_request
属性中获取指定的属性,使用getattr(self._request, attr)
。 - 如果成功获取到属性,则返回它的值。
- 如果发生
AttributeError
异常(即_request
中不存在该属性),则捕获异常。 - 在异常处理中,调用
self.__getattribute__(attr)
直接访问对象本身的属性。 - 如果仍然发生
AttributeError
异常,则说明对象本身也没有这个属性,将会抛出原始的AttributeError
异常。
- 首先,尝试从
- 魔法方法补充:
__getattr__
- . 拦截(对象.属性)
- 当属性不存在时,会触发 类中
__getattr__
的执行
【3】Request 中的 query_params
方法
@property
def query_params(self):
return self._request.GET
query_params
是一个只读属性(使用@property
装饰器),用于获取请求中的查询参数。
代码逻辑如下:
-
属性装饰器
@property
用于将方法转换为只读属性。- 这样,在访问
query_params
属性时,实际上是调用该方法并返回其结果。
- 这样,在访问
-
query_params
方法返回了self._request.GET
,其中_request
是一个私有属性,可能是指向当前请求对象的引用。- 在 Django 中,请求对象有一个名为
GET
的属性,用于获取请求的查询参数。
- 在 Django 中,请求对象有一个名为
- 假设
_request
是一个指向请求对象的引用,上述代码可以有效地将请求对象的GET
属性作为查询参数返回给调用方。- 通过访问
query_params
属性,可以获得请求中包含的所有查询参数的字典形式。
【补充】魔法方法 __getattr__
-
__getattr__
是一个特殊方法(也称为魔法方法或特殊属性),可以用于在访问一个对象的不存在的属性时进行拦截和处理。 -
当我们使用"对象.属性"的形式访问对象的属性时
- 如果该属性不存在,Python 会默认触发
__getattr__
方法的执行。
- 如果该属性不存在,Python 会默认触发
-
__getattr__
方法接收一个参数,即要访问的属性名,可以在这个方法中定义自定义的逻辑来处理这种情况。- 它允许我们动态地添加属性或者对属性访问进行处理。
【1】__getattr__
方法的定义:
def __getattr__(self, name):
# 自定义处理逻辑
【2】触发条件:
- 当通过对象访问属性时
- 如果该属性不存在,就会触发
__getattr__
方法的执行。
【3】参数:
self
:对象本身。name
:要访问的属性名。
【4】返回值:
- 可以返回一个属性值,以满足属性访问的需求。
- 如果仍然不存在属性,则引发
AttributeError
异常。
【5】示例:
下面的示例演示了如何使用 __getattr__
方法来动态处理属性的访问:
class MyClass:
def __getattr__(self, name):
if name == 'attribute1':
return 'This is attribute1'
elif name == 'attribute2':
return 'This is attribute2'
else:
raise AttributeError(f"'MyClass' object has no attribute '{name}'")
obj = MyClass()
print(obj.attribute1) # 输出:This is attribute1
print(obj.attribute2) # 输出:This is attribute2
print(obj.attribute3) # 输出:AttributeError: 'MyClass' object has no attribute 'attribute3'
-
在上述示例中
- 当访问
obj.attribute1
或者obj.attribute2
时,__getattr__
方法会被触发并返回相应的值。
- 当访问
-
但是当访问一个不存在的属性时
- 如
obj.attribute3
,__getattr__
方法引发AttributeError
异常。
- 如
-
通过使用
__getattr__
方法,我们可以对属性访问进行灵活的处理,例如实现延迟加载、使用动态属性等功能。
【补充】Http的get请求可不可以携带body数据
可以带,但是不建议带
- 根据HTTP/1.1和HTTP/2协议规范,GET请求通常不包含请求体(body),而是将参数以键值对的形式添加到URL的查询字符串中。
- GET请求的主要目的是从服务器获取资源或数据
- 并且请求参数会被编码在URL中
- 而非请求体中。
- 然而,虽然不建议在GET请求的请求体中携带数据,但HTTP协议本身并没有明确禁止该操作。
- 一些服务器和应用程序可能会接受具有请求体的GET请求,并根据具体实现来处理这些请求。
- 由于GET请求的设计初衷是用于数据获取而非数据提交,因此将数据放入请求体中可能会导致一些问题。
- 例如,一些服务器可能会忽略请求体中的数据或拒绝处理带有请求体的GET请求。
- 总结起来
- 尽管HTTP的GET请求通常不应该在请求体中携带数据,但是一些特定情况下,服务器和应用程序可能会支持处理带有请求体的GET请求。
- 使用时请注意遵循HTTP协议的规范,并确保与服务器端的需求相匹配。
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17593137.html