Python| Drf常用view解析

1 APIView
drf提供了一个继承自View类的APIView类,它比View多了一些好用的方法。
- 由APIView来对请求对象进行再次封装,将django的HttpRequest变为drf的Request
- 返回Response而不是HttpResponse
- 捕获所有的APIException并进行响应
- 传入的请求通过APIView来进行认证、权限验证等操作
1.1 APIView的AOP
APIView保持了View的特性,dispatch方法在APIView里也存在,它按照不同的请求方法将请求分发至某个函数来处理。
继承自APIView的视图可以设置很多AOP(类属性)来进行认证、权限、频率、渲染等控制。
- .render_classes:设置渲染器
- .parser_classes:设置解析器
- .authentication_classes:设置认证器
- .throttle_classes:设置频率控制器
- .permission_classes:设置权限控制器
1.2 策略方法
APIView也提供了一些策略方法:
- .get_renderers(self)
- .get_parsers(self)
- .get_authenticators(self)
- .get_throttles(self)
- .get_permissions(self)
- .get_content_negotiator(self)
- .get_exception_handler(self)
这些策略方法会返回解析器、渲染器、频率控制器、权限控制器这些AOP的可调用对象列表:
def get_renderers(self):
"""
Instantiates and returns the list of renderers that this view can use.
"""
return [renderer() for renderer in self.renderer_classes]
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
return [parser() for parser in self.parser_classes]
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
def get_throttles(self):
"""
Instantiates and returns the list of throttles that this view uses.
"""
return [throttle() for throttle in self.throttle_classes]
1.3 控制策略实现
APIView如何实现权限、频率控制呢?在APIView内部实现了一些方法,在request分派到具体的处理方法(get、post)之前,它会自动调用一些策略进行验证:
- .check_permissions(self,request)
- .check_throttles(self,request)
- .perform_content_negotiation(self,request,force=False)
这些方法的实现借助了上面提到的策略方法,比如在check_permissions中
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
check_permissions方法接收APIView封装的request,然后调用get_permissions()方法获取视图中允许的所有权限控制类,然后进行权限验证。
1.4方法调度中所做的工作
dispatch方法在APIView中起到request再封装、权限、认证、频率控制、方法调度的作用。
def dispatch(self, request, *args, **kwargs):
"""
`.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
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
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中的dispatch对View的dispatch再次进行了改造,下面的这些方法起到了核心作用:
-
initialize_request(self,request,*args,**kwargs)
initialize_request做了以下的工作,可以看到,它对request进行了再次封装,让request变成了drf中的Request
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
2、initial(request, *args, **kwargs)
在initial方法中,我们可以看到它在最后做了三个操作:
- perform_authentication 认证
- check_permissions 检查频率
- check_throttles 检查频率
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
进行完request再封装和权限、认证、频率检查后dispatch开始进行方法调度,可以看到他会对不存在/不合法的请求方法进行对应的非法响应,将正确的响应通过反射分发到不同的方法进行处理。
2 GenericAPIView
GenericAPIView是APIView的子类,它添加了一些标准列表和详细视图的行为。
在rest_framework中的generic模块中定义了GenericAPIView类,这个类提供了一些有关数据集、URL参数、过滤器、分页器等属性和方法:
# 类属性
queryset = None
serializer_class = None
# If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None
# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# The style to use for queryset pagination.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

通过GenericAPIView提供的这些方法和属性,可以帮助我们用更少的业务代码编写相关业务逻辑。
使用GnericAPIView进行数据分页
class demo(generics.GenericAPIView):
queryset=clubAdmin.objects.all()
serializer_class = clubAdminSerializer
def get(self,request,*args,**kwargs):
users = self.get_queryset()
pager_users = self.paginate_queryset(queryset=users)
user_sers = self.get_serializer(instance = pager_users,many=True)
return Response(user_sers.data)
在业务视图中,首先设置queryset数据集、序列化类,并且使用默认的分页器
然后定义get方法,在get方法中调用GnericAPIView中相关的方法来获取最终序列化的分页数据并返回
到此为止,其实GenericAPIView的编写模式和APIView的编写模式具体查询逻辑没有太大区别,还是需要我们去查询数据实例、进行分页、然后再序列化。
3 GenericViewSet
GenericViewSet继承自GenericAPIView和ViewSetMixin类,并且在GnericViewSet类本身没有实现什么具体的方法。
所以只能去关注ViewSetMixin类中封装了一些什么:
"""
This is the magic.
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions...
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
在ViewSetMixin类的注释中是这样描述的:ViewSetMixin重写了.as_view()方法以至于可以提供一种http方法到我们自定义方法名的映射关系。在使用ViewSetMixin视图时,需要在as_view()中传入一个字典,这个字典就是我们定义的http方法到视图方法的映射。
所以GenericViewSet集合了ViewSetMixin和GenericAPIView的所有方法,它可以提供自定义视图请求方法名,也可以支持GenericAPIView中提供的规范化通用视图操作。
4 Mixins和ModelViewSet
minxins中提供的各种mixin类提供了基本的通用视图行为操作(增删改查)
以ListModelMixin为例,我们先来分析一下它到底做了什么
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
ListModelMixin类提供了一个list方法,它在内部利用一些在GenericAPIView类中实现的方法,帮我们把queryset中的数据进行分页、序列化操作后直接产生响应。
所以只需要将我们的视图继承自ListModelMixin和GenericViewSet并且设置好queryset、pagination_class 、serializer_class以及as_view()中get到list的方法映射,视图类就可以直接访问到ListModelMixin的list方法自动帮我们产生所有数据的响应,减少了很多代码冗余。
像ListModelMixin一样的类还有很多:
class CreateModelMixin:
"""
增加一个数据
"""
class RetrieveModelMinxin:
"""
查询单条数据
"""
class ListModelMixin:
"""
查询列表数据
"""
class UpdateModelMixin:
"""
更新一条数据
"""
class DestroyModelMixin:
"""
删除一条数据
"""
在很多场景都需要针对某个数据做增删改查操作,ModelViewSet就帮我们直接封装好了增删改查的基本业务操作:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
到底是全量继承自ModelViewSet还是按需继承自mixins这就要看具体的业务需求。