path('text_Apiview/', views.TextAPIView.as_view()),
from rest_framework.views import APIView
from rest_framework.response import Response
class TextAPIView(APIView):
def get(self, request):
return Response(data={'msg': 'ok'})
1. @classmethod
class APIView(View):
# APIVie继承了django原生View
@classmethod
# 1. 相对于diango原生View,APIView用了纯正的类方法
# 2. 此时as_view是一个类方法
# 3. 类方法会把其调用者,当作第一个参数传进来
2. as_view---->view
def as_view(cls, **initkwargs):
# 1. cls-->由于as_view是一个类的绑定方法,类调用的时候,会把类传进来,
# 2. 那么cls就是我们当前调用的类--->TextAPIView
# 3. 他这一步就是把我们的TextView类加括号,实例化成对象(我们下列用TA对象代替他)
# 4. initkwargs--->一个普通的形参,只是名字好看一点,把它当成**kwargs即可
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
# 1. 进行判断,首先通过反射从TA对象获取queryset,然后判断是否是QuerySet对象类型
def force_evaluation():
# 2. 是抛异常
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
# 1. 把异常封装成TA对象的属性
view = super().as_view(**initkwargs)
# 1. 执行父类的(django原生的View)--->我们在CBV源码分析中提到,
# 2. django原生的as_view其实就是返回了一个view内层函数
3. view = super().as_view(**initkwargs)--->执行其父类的as_view
def as_view(cls, **initkwargs):
# 1. initkwargs--->一个普通的形参,只是名字好看一点,把它当成**kwargs即可
"""Main entry point for a request-response process."""
for key in initkwargs:
# 1. 这一步循环取值并判断,其实对我们来讲意义不大,只要知道,as_view()支持以字典的形式传参,例如:as_view({'get':'xxx'})
# 2. 如果该形参有值,进行循环取值,并判断,如果传的值在http_method_names列表中的话,抛异常
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
# 1. 进行判断,比if高级了一点,判断当前传入的值,如果不在当前类中(当前类没有,去其父类,也就是,我们继承的View中找),抛异常
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
# 1. 利用闭包的思想(闭包的最基本的概念:在函数内部定义的变量对外部有调用)
# 2. 提前说一下,as_view最终返回一个view的内存地址,
# 3. 我们一开始就说了,django路由规定,路由的第二个参数必须放一个函数的内存地址/include,这个符合django的路由规定
# 4. 绕了一圈,我们知道,原来as_view(),加括号执行后,居然返回一个内层函数view,并且as_view支持以字典的方式传参
self = cls(**initkwargs)
# 1. cls-->由于as_view是一个类的绑定方法,类调用的时候,会把类传进来,
# 2. 那么cls就是我们当前调用的类--->TextView
# 3. 他这一步就是把我们的TextView类加括号,实例化成对象(我们下列用TV对象代替他)
if hasattr(self, 'get') and not hasattr(self, 'head'):
# 1. 判断get是TV对象对象的方法
# 2. head不是TV对象对象的方法
self.head = self.get
# 如果成立给TV对象增加一个实例属性,{'head':'get'}
self.setup(request, *args, **kwargs)
# 1. 调用TV对象的setup方法(TV对象中没有,去View中去找)
if not hasattr(self, 'request'):
# 1. 老套路,判断request是否是TV对象的属性
# 2. 不是抛异常
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
# 1. 是的话,执行dispatch方法,
# 2. 说一下执行流程,首先去对象里面找(没有)--在去类中找(没有)--在去其父类中找(没有)--父类的父类,一直找到根(还没有报错,xxx没有这个属性)
# 最终返回 handler方法
# 3. handler-->存的是你在视图中写的 get/post...
# 4. 或者是 异常方法
# **************
# 1.重点来了,我在说一边,执行顺序
# 2. 首先去对象里面找(没有)--在去类中找(没有)--在去其父类中找(没有)--父类的父类,
# 3. 一直找到根(还没有报错,xxx没有这个属性)
# 4. 也就是说现在的dispatch,是APIView中的dispatch(TA对象与类中没有,只能找其父类)
return self.dispatch(request, *args, **kwargs)
4. return self.dispatch(request, *args, **kwargs)--->父类的dispatch,不在是django原生View
def dispatch(self, request, *args, **kwargs):
# 1. APIView中的dispatch
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 1. 当前self--->TV的对象
# 2. 给TV对象封装数据属性
request = self.initialize_request(request, *args, **kwargs)
# 1. 这个request 是新的request,********* 妙 **********
# 2. APIView对request进行了一层封装,
# 3. 我解释一下原生的request,request-->经过wsgi解析成一个字典--->django又封装成了一个对象
5. request = self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 把django原生的request给当作第一个参数传递给了Request
return Request(
request, # django原生的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
6. 我们看看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__)
)
# 1. 这个是django原生的request,被重新封装了一下,
# 2. 也就是说,我们想要在视图中,获取原生的request的话,
# 3. request._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
7. 新的reauest,一些属性和方法
a. 重写了__getattr__(目的是为了原生的request的一些属性和方法,继续可以使用,比如,request.GET/POST,当你用句点符操作的时候,会自动执行该魔术方法,他会去原生的request中取值)
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
b. request.data(request.data 感觉是个数据属性,其实是个方法,@property,修饰了
它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data)
post:提交的三种编码格式:urlencode,form-data,json,django只能处理前两种,最后一种,需要自己去request.body自己去取
post三种编码格式http://xn--14q306a625a
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
self._full_data
self._full_data = Empty
Empty
class Empty:
"""
Placeholder for unset attributes.
Cannot use `None`, as that may be a valid value.
"""
pass
c. request.GET(被重写赋值)
request.GET
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
# APIView的dispatch方法
def dispatch(self, request, *args, **kwargs):
try:
# 三大认证模块
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 响应模块
response = handler(request, *args, **kwargs)
except Exception as exc:
# 异常模块
response = self.handle_exception(exc)
# 渲染模块
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
# APIView的initial方法
def initial(self, request, *args, **kwargs):
# 认证组件:校验用户 - 游客、合法用户、非法用户
# 游客:代表校验通过,直接进入下一步校验(权限校验)
# 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
# 非法用户:代表校验失败,抛出异常,返回403权限异常结果
self.perform_authentication(request)
# 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
# 认证通过:可以进入下一步校验(频率认证)
# 认证失败:抛出异常,返回403权限异常结果
self.check_permissions(request)
# 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
# 没有达到限次:正常访问接口
# 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
self.check_throttles(request)