restframework 源码分析以及使用

mixins 中的五种类方法

from rest_framework import mixins
# mixins 中一种有五种类
# 第一种:用户保存数据
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 序列化的类的对象
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) # 校验
self.perform_create(serializer) # 保存
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer):
serializer.save() # 保存 def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
# 第二种:用户取出多条数据
class ListModelMixin(object):
"""
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)
# 返回分页相关序列化对象的data
return self.get_paginated_response(serializer.data)
# 正常序列化
serializer = self.get_serializer(queryset, many=True)
# 正常返回
return Response(serializer.data)
'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'
# 第三种:用于取出单个数据
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
# 第四种:用户修改更新数据
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {} return Response(serializer.data) def perform_update(self, serializer):
serializer.save() def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
# 第五种: 用于删除数据
class DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance):
instance.delete()

GenericAPIView类源码

from rest_framework.generics import GenericAPIView
class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
# `get_queryset()` instead of accessing the `queryset` property directly,
# as `queryset` will get evaluated only once, and those results are cached
# for all subsequent requests.
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 def get_queryset(self):
"""
Get the list of items for this view.
This must be an iterable, and may be a queryset.
Defaults to using `self.queryset`. This method should always be used rather than accessing `self.queryset`
directly, as `self.queryset` gets evaluated only once, and those results
are cached for all subsequent requests. You may want to override this if you need to provide different
querysets depending on the incoming request. (Eg. return a list of items that is specific to the user)
"""
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
# 这里的 self.queryset 根据 调用的时候 传值的querset来的
queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset # 返回的 Querset对象 def get_object(self):
"""
Returns the object the view is displaying. You may want to override this if you need to provide non-standard
queryset lookups. Eg if objects are referenced using multiple
keyword arguments in the url conf.
"""
queryset = self.filter_queryset(self.get_queryset()) # Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
) filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs) # May raise a permission denied
self.check_object_permissions(self.request, obj) return obj def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
# 通过调用 get_serializer_class() 获取 序列化类
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
# 返回序列化类的实例对象
return serializer_class(*args, **kwargs) def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`. You may want to override this if you need to provide different
serializations depending on the incoming request. (Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
# 返回的是序列化的类
return self.serializer_class def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
} def filter_queryset(self, queryset):
"""
Given a queryset, filter it with whichever filter backend is in use. You are unlikely to want to override this method, although you may need
to call it either from a list view, or from a custom `get_object`
method if you want to apply the configured filtering backend to the
default queryset.
"""
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset @property
def paginator(self):
"""
The paginator instance associated with the view, or `None`.
"""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator def paginate_queryset(self, queryset):
"""
Return a single page of results, or `None` if pagination is disabled.
"""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self) def get_paginated_response(self, data):
"""
Return a paginated style `Response` object for the given output data.
"""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)

视图类的四种方法

路由(前三种写法)

    url(r'^books/$',views.BooksView.as_view()),
url(r'^books/(?P<pk>\d+)$',views.BookDetailView.as_view())
  • 基本写法(继承APIView)

    # 写一个出版社的增删查改resful接口
    
    """路由"""
    url(r'^publish/$', views.PublishView.as_view()),
    url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailView.as_view()), """视图"""
    class PublishSerializers(serializers.ModelSerializer):
    class Meta:
    model=models.Publish
    fields='__all__' class PublishView(APIView): def get(self, request):
    publish_list = models.Publish.objects.all()
    bs = PublishSerializers(publish_list, many=True)
    # 序列化数据 return Response(bs.data) def post(self, request):
    # 添加一条数据
    print(request.data) bs=PublishSerializers(data=request.data)
    if bs.is_valid():
    bs.save() # 生成记录
    return Response(bs.data)
    else: return Response(bs.errors) class PublishDetailView(APIView):
    def get(self,request,pk):
    publish_obj=models.Publish.objects.filter(pk=pk).first()
    bs=PublishSerializers(publish_obj,many=False)
    return Response(bs.data)
    def put(self,request,pk):
    publish_obj = models.Publish.objects.filter(pk=pk).first() bs=PublishSerializers(data=request.data,instance=publish_obj)
    if bs.is_valid():
    bs.save() # update
    return Response(bs.data)
    else:
    return Response(bs.errors)
    def delete(self,request,pk):
    models.Publish.objects.filter(pk=pk).delete() return Response("")
  • 第二种写法

    from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \ ListModelMixin, RetrieveModelMixin
    
    from rest_framework.generics import GenericAPIView
    from app01 import myser
    from app01 import models class BooksView(CreateModelMixin, ListModelMixin, GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = myser.BookSerializer def post(self, request, *args, **kwargs):
    print(request.data)
    return self.create(request, *args, **kwargs) # 返回多条数据
    def get(self, request, *args, **kwargs):
    return self.list(request, *args, **kwargs) class BookDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = myser.BookSerializer # 返回单条数据
    def get(self, request, *args, **kwargs):
    return self.retrieve(request, *args, **kwargs) # 修改数据
    def put(self, request, *args, **kwargs):
    return self.update(request, *args, **kwargs) # 删除数据
    def delete(self, request, *args, **kwargs):
    return self.destroy(request, *args, **kwargs)
  • 第三种写法

    class BooksView(ListCreateAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers
  • 第四种写法(两个视图类合成一个)

    路由

     url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
    url(r'^publish/(P<pk>\d+)/$',views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
    from rest_framework.viewsets import ModelViewSet
    class PublishView(ModelViewSet):
    queryset=models.Publish.objects.all()
    serializer_class=PublishSerializers
    # ViewSetMixin重写了as_view方法,路由配置就改了,可以写成映射的形式{get:get_one}
    # as_view方法内部执行效果
    # 通过反射的取值跟赋值,完成映射,根据请求方式执行对应的方法(比如:get请求执行get_one方法,在路由中配置{get:getone})

ModelViewSet源码流程分析

"""
ModelViewSet:
1.----> 首先 源码中ModelViewSet继承了如下mixins.CreateModelMixin....等五种Mixin方法,最重要的变化是继承了 GenericViewSet
2.----> 进入 GenericViewSet 继承了 ViewSetMixin, generics.GenericAPIView 变化在于继承的ViewSetMixin
3.----> 进入 ViewSetMixin 和 APIView 区别就在与重写了 as_view 方法
4.---->
5.---->
6.---->
7.---->
8.---->
9.---->
10.--->
""" 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
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass class ViewSetMixin(object):
"""
This is the magic.
重写了 as_view方法 目的是为了获得一个个key对应一个个 Http请求的方法的映射关系
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'})
"""
# 这里是重写的 as_view方法 ,传入 action-即为 前面 as_view({'get':'list',})
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The name and description initkwargs may be explicitly overridden for
# certain route confiugurations. eg, names of extra actions.
cls.name = None
cls.description = None # The suffix initkwarg is reserved for displaying the viewset type.
# This initkwarg should have no effect if the name is provided.
# eg. 'List' or 'Instance'.
cls.suffix = None # The detail initkwarg is reserved for introspecting the viewset type.
cls.detail = None # Setting a basename allows a view to reverse its action urls. This
# value is provided by the router through the initkwargs.
cls.basename = None # actions must not be empty
# 这里 actions 不能为空,所以 as_view({'..':'..'}) 必须穿参数
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`") # sanitize keyword arguments
for key in initkwargs:
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__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key)) # name and suffix are mutually exclusive
if 'name' in initkwargs and 'suffix' in initkwargs:
raise TypeError("%s() received both `name` and `suffix`, which are "
"mutually exclusive arguments." % (cls.__name__)) def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
# 将 actions 传过来的字典映射关系给 self.action_map
self.action_map = actions # Bind methods to actions
# This is the bit that's different to a standard view
# 这里就是和标准的as_view 不同之处
for method, action in actions.items():
# 通过反射获取对象中 actions的key(这里赋值给了action)的方法
handler = getattr(self, action) # 获得是内存地址
setattr(self, method, handler) # 将内存地址赋值给 method 也就是此时method方法就是handler方法 if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get self.request = request
self.args = args
self.kwargs = kwargs # And continue as usual
return self.dispatch(request, *args, **kwargs) # take name and docstring from class
update_wrapper(view, cls, updated=()) # and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=()) # We need to set these on the view function, so that breadcrumb
# generation can pick out these bits of information from a
# resolved URL.
view.cls = cls
view.initkwargs = initkwargs
view.actions = actions
return csrf_exempt(view)

对比 APIView 中的 as_view()

   from rest_framework.views import APIView
# APIView 中的 as_view()
@classmethod
def as_view(cls, **initkwargs):
"""
Store the original class on the view function. This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
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 view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)

Django 之restfromwork 源码分析以及使用之--视图组件的更多相关文章

  1. Django搭建及源码分析(三)---+uWSGI+nginx

    每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...

  2. Django如何启动源码分析

    Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字 ...

  3. Django之DRF源码分析(二)---数据校验部分

    Django之DRF源码分析(二)---数据校验部分 is_valid() 源码 def is_valid(self, raise_exception=False): assert not hasat ...

  4. Django 之restfromwork 源码---APIView 分析

    Django 之 djangorestframework的APIView分析 APIView 类中的as_view() 方法 首先 我们从视图函数入手,在urls.py 中的 URLconfig中添加 ...

  5. Django rest framework源码分析(3)----节流

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  6. Django rest framework源码分析(1)----认证

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  7. Django rest framework 源码分析 (1)----认证

    一.基础 django 2.0官方文档 https://docs.djangoproject.com/en/2.0/ 安装 pip3 install djangorestframework 假如我们想 ...

  8. Django rest framework源码分析(一) 认证

    一.基础 最近正好有机会去写一些可视化的东西,就想着前后端分离,想使用django rest framework写一些,顺便复习一下django rest framework的知识,只是顺便哦,好吧. ...

  9. Django中间件部分源码分析

    中间件源码分析 中间件简介 中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Django的输入和输出.每个中间件组件都负责做一些特定的 ...

随机推荐

  1. .NET基础知识(02)-拆箱与装箱

    装箱和拆箱几乎是所有面试题中必考之一,看上去简单,就往往容易被忽视.其实它一点都不简单的,一个简单的问题也可以从多个层次来解读. 常见面试题目: 1.什么是拆箱和装箱? 2.什么是箱子? 3.箱子放在 ...

  2. 安卓设备连接Mac的简单方法

    mac设备是苹果出品的桌面系统,以高冷而闻名,不同于我们平常使用的windows系统,mac系统对软件硬件的兼容性很差,将iOS 设备(iPhone.iPad和iPod)连接至Mac是一件很简单的事, ...

  3. [原创]Emmagee V2.4工具使用介绍

    [原创]Emmagee V2.4工具使用介绍 1 Emmagee 介绍 Emmagee 是网易杭州研究院 QA团队开发的一款简单易上手的Android性能监控App,主要用于监控单个App的CPU.内 ...

  4. mysql(七)查询基本语法

    连接 外连接 如表-------------------------------------------------table1 | table2 |------------------------- ...

  5. 【Gamma】Scrum Meeting 6

    前言 会议定点:大运村公寓 会议时间:2019/6/4 会议目的:明确测试中出现的bug 一.任务进度 组员 上周任务进度 下阶段任务 大娃 优化代码注释质量 修复后端bug 二娃 撰写会议博客 撰写 ...

  6. 1-7docke的网络模式

    1.Bridge模式 bridge 模式是 docker 的默认⽹络模式,不写 –net 参数,就是 bridge 模式.比如使⽤ docker run - p 时 工作模式从网上找了一个,如下 例子 ...

  7. 文本编辑 工具vim

    一vim的3种常用模式:一般模式.编辑模式和命令模式 如果系统里没有vim工具,需安装 1,一般模式 首先复制一个文件到/tmp/目录下,并改名为1,txt 移动光标: h 或 向左箭头键(←)    ...

  8. 第九次作业 DFA最小化,语法分析初步

    1.将DFA最小化:教材P65 第9题 Ⅰ {1,2,3,4,5} {6,7} {1,2}b={1,2,3,4,5} 3,4}b={5} {6,7} Ⅱ {1,2}{3,4}{5} {6,7} 2.构 ...

  9. 认识一下microbit扩展板robotbit

    在我们买到的套件中,另一块叫robotbit扩展板,所谓扩展板,是把控制板上的针脚引出来,方便我们扩展驱动连接电机.舵机.及各种传感器以展功能. 介绍如下 :下面 反面: 官方文档:http://le ...

  10. java -- eclipse运行javaweb 项目

    这个是和上一个放在一块的 创建javaweb项目,要是想要创建maven项目,java项目都可以,我要和tomcat放在一块所以 就创建javaweb项目 创建项目起一个有意义的项目名    选择一个 ...