GVKun编程网logo

Rest_Framework的视图与路由(rest framework)

7

在本文中,我们将详细介绍Rest_Framework的视图与路由的各个方面,并为您提供关于restframework的相关解答,同时,我们也将为您带来关于Django-rest_framework、D

在本文中,我们将详细介绍Rest_Framework的视图与路由的各个方面,并为您提供关于rest framework的相关解答,同时,我们也将为您带来关于Django - rest_framework、Django REST Framework 批量更新 rest_framework_extensions、Django Rest Framework源码剖析(八)-----视图与路由、Django REST_framework 路由控制的有用知识。

本文目录一览:

Rest_Framework的视图与路由(rest framework)

Rest_Framework的视图与路由(rest framework)

视图与路由

drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作。所以在django原有的django.views.View类基础上,drf封装了多个子类出来提供给我们使用。

Django REST framwork 提供的视图的主要作用:

  • 控制序列化器的执行(检验、保存、转换数据)
  • 控制数据库查询的执行
  • 调用请求类和响应类[这两个类也是由drf帮我们再次扩展了一些功能类]

先创建一个子应用httpdemo

python manage.py startapp httpdemo

1.1. 请求与响应

1.1.1 Request

REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。

REST framework 提供了Parser解析器类,在接收到request请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。

Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。

无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。

1.1.1.1 常用属性

1).data

request.data 返回解析之后的请求体数据。类似于Django中标准的request.POSTrequest.FILES属性,但提供如下特性:

  • 包含了解析之后的文件和非文件数据
  • 包含了对POST、PUT、PATCH请求方式解析后的数据
  • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2).query_params

request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已。

1.1.2 Response

rest_framework.response.Response

REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染器对象)成符合前端需求的类型。

REST framework提供了Render 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。

restframework/settings.py

# 默认响应渲染类
''DEFAULT_RENDERER_CLASSES'': [
    # json渲染器
    ''rest_framework.renderers.JSONRenderer'',
    # 浏览API渲染器
    ''rest_framework.renderers.BrowsableAPIRenderer'',
],

1.1.2.1 构造方式

Response(data, status=None, template_name=None, headers=None, content_type=None)

data数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer渲染器处理data

data不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer序列化器序列化处理后(转为了Python字典类型)再传递给data参数。

参数说明:

  • data: 为响应准备的序列化处理后的数据;
  • status: 状态码,默认200;
  • template_name: 模板名称,如果使用HTMLRenderer 时需指明;
  • headers: 用于存放响应头信息的字典;
  • content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。

1.1.2.2 常用属性

注意,这些属性只有在视图函数之后才可以调用

1).data

传给response对象的序列化后,但尚未render处理的数据

2).status_code

状态码的数字

3).content

经过render处理后的响应数据

1.1.2.3 状态码

为了方便设置状态码,REST framewrok在rest_framework.status模块中提供了常用状态码常量。

1)信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
2)成功 - 2xx
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
4)客户端错误 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
5)服务器错误 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

1.2 视图

REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。

为了区分上面请求和响应的代码,我们再次创建一个新的子应用:

python manage.py startapp baseview

1.2.1 2个视图基类

1.2.1.1 APIView

rest_framework.views.APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

APIViewView的不同之处在于:

  • 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
  • 视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
  • 任何APIException异常都会被捕获到,并且处理成合适的响应信息;
  • 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。

支持定义的属性

  • authentication_classes 列表或元祖,身份认证类
  • permissoin_classes 列表或元祖,权限检查类
  • throttle_classes 列表或元祖,流量控制类

APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。

举例:

from rest_framework.views import APIView
from rest_framework.response import Response

# url(r''^books/$'', views.BookView.as_view()),
class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)

1.2.1.2 GenericAPIView[通用视图类]

rest_framework.generics.GenericAPIView

继承自APIVIew主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。

提供的关于序列化器使用的属性与方法

  • 属性:

    • serializer_class 指明视图使用的序列化器
  • 方法:

    • get_serializer_class(self)

      当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。

      返回序列化器类,默认返回serializer_class,可以重写,例如:

      def get_serializer_class(self):
          if self.request.user.is_staff:
              return FullAccountSerializer
          return BasicAccountSerializer
      
    • get_serializer(self, *args, **kwargs)

      返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。

      注意,该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。

      • request 当前视图的请求对象
      • view 当前请求的类视图对象
      • format 当前请求期望返回的数据格式

提供的关于数据库查询的属性与方法

  • 属性:

    • queryset 指明使用的数据查询集
  • 方法:

    • get_queryset(self)

      返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:

      def get_queryset(self):
          user = self.request.user
          return user.accounts.all()
      
    • get_object(self)

      返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。

      在试图中可以调用该方法获取详情信息的模型类对象。

      若详情访问的模型类对象不存在,会返回404。

      该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。

      举例:

      # url(r''^books/(?P<pk>\d+)/$'', views.BookDetailView.as_view()),
      class BookDetailView(GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象
              serializer = self.get_serializer(book)
              return Response(serializer.data)
      

其他可以设置的属性

  • pagination_class 指明分页控制类
  • filter_backends 指明过滤控制后端

1.2.1.3 视图扩展类

在drf中提供了5个不同的视图扩展类吗,里面封装了5个不同的http请求方法,用于和GenericAPIView组合实现代码的简写

CreateModelMixin[添加一条数据]

ListModelMixin[获取所有数据]

RetrieveModelMixin[获取一条数据]

DestroyModelMixin[删除一条数据]

UpdateModelMixin[更新一条数据]

drf中对视图扩展类和GenericAPIView的组合进行了进一步的简写,衍生出了多个视图子类

作用:

提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。

1)ListModelMixin

列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。

该Mixin的list方法会对数据进行过滤和分页。

源代码:

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)
            return self.get_paginated_response(serializer.data)
        # 序列化
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

举例:

from rest_framework.mixins import ListModelMixin

class BookListView(ListModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request):
        return self.list(request)

2)CreateModelMixin

创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。

如果序列化器对前端发送的数据验证失败,返回400错误。

源代码:

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 {}

3)RetrieveModelMixin

详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。

如果存在,返回200, 否则返回404。

源代码:

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 BookDetailView(RetrieveModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        return self.retrieve(request)

4)UpdateModelMixin

更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。

同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

成功返回200,序列化器校验数据失败时,返回400错误。

源代码:

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)

5)DestroyModelMixin

删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。

成功返回204,不存在返回404。

源代码:

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和视图扩展类,实现api接口,代码:

"""GenericAPIView结合视图扩展类实现api接口"""
from rest_framework.mixins import ListModelMixin,CreateModelMixin
class Students2GenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
    # 本次视图类中要操作的数据[必填]
    queryset = Student.objects.all()
    # 本次视图类中要调用的默认序列化器[选填]
    serializer_class = StudentModelSerializer

    def get(self, request):
        """获取多个学生信息"""
        return self.list(request)

    def post(self,request):
        """添加学生信息"""
        return self.create(request)


from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student2GenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()

    serializer_class = StudentModelSerializer

    # 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk
    def get(self,request,pk):
        """获取一条数据"""
        return self.retrieve(request,pk)

    def put(self,request,pk):
        """更新一条数据"""
        return self.update(request,pk)

    def delete(self,request,pk):
        """删除一条数据"""
        return self.destroy(request,pk)

1.2.3 GenericAPIView的视图子类

1)CreateAPIView

提供 post 方法

继承自: GenericAPIView、CreateModelMixin

2)ListAPIView

提供 get 方法

继承自:GenericAPIView、ListModelMixin

3)RetrieveAPIView

提供 get 方法

继承自: GenericAPIView、RetrieveModelMixin

4)DestoryAPIView

提供 delete 方法

继承自:GenericAPIView、DestoryModelMixin

5)UpdateAPIView

提供 put 和 patch 方法

继承自:GenericAPIView、UpdateModelMixin

6)RetrieveUpdateAPIView

提供 get、put、patch方法

继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7)RetrieveUpdateDestoryAPIView

提供 get、put、patch、delete方法

继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

1.3 视图集ViewSet

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 保存数据
  • destory() 删除数据

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:

class BookInfoViewSet(viewsets.ViewSet):

    def list(self, request):
        books = BookInfo.objects.all()
        serializer = BookInfoSerializer(books, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        try:
            books = BookInfo.objects.get(id=pk)
        except BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookInfoSerializer(books)
        return Response(serializer.data)

在设置路由时,我们可以如下操作

urlpatterns = [
    url(r''^books/$'', BookInfoViewSet.as_view({''get'':''list''}),
    url(r''^books/(?P<pk>\d+)/$'', BookInfoViewSet.as_view({''get'': ''retrieve''})
]

1.3.1 常用视图集父类

1) ViewSet

继承自APIViewViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{''get'':''list''})的映射处理工作。

在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

2)GenericViewSet

使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView

GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIViewViewSetMixin,在实现了调用as_view()时传入字典(如{''get'':''list''})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。

举例:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

url的定义

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),

]

3)ModelViewSet

继承自GenericViewSet,同时包括了、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

4)ReadOnlyModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

1.3.2 视图集中定义附加action动作

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。

举例:

from rest_framework.decorators import action
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    
    def login(self,request):
        """学生登录功能"""
        return Response({"message":"登录成功"})

url的定义

urlpatterns = [
    path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students8/(?P<pk>\d+)/",
            views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),

    path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"}))

]

1.3.3 action属性

在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。

例如:

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def get_new_5(self,request):
        """获取最近添加的5个学生信息"""
        # 操作数据库
        print(self.action) # 获取本次请求的视图方法名
        
        
通过路由访问到当前方法中.可以看到本次的action就是请求的方法名

2. 路由Routers

对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。

REST framework提供了两个router

  • SimpleRouter
  • DefaultRouter

2.1 使用方法

1) 创建router对象,并注册视图集,例如

from rest_framework import routers

router = routers.DefaultRouter()
router.register(r''router_stu'', StudentModelViewSet, base_name=''student'')

register(prefix, viewset, base_name)

  • prefix 该视图集的路由前缀
  • viewset 视图集
  • base_name 路由别名的前缀

如上述代码会形成的路由如下:

^books/$    name: book-list
^books/{pk}/$   name: book-detail

2)添加路由数据

可以有两种方式:

urlpatterns = [
    ...
]
urlpatterns += router.urls

urlpatterns = [
    ...
    url(r''^'', include(router.urls))
]

使用路由类给视图集生成了路由地址

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def login(self,request):
        """学生登录功能"""
        print(self.action)
        return Response({"message":"登录成功"})

路由代码:

from django.urls import path, re_path
from . import views
urlpatterns = [
    ...
]

"""使用drf提供路由类router给视图集生成路由列表"""
# 实例化路由类
# drf提供一共提供了两个路由类给我们使用,他们用法一致,功能几乎一样
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

# 注册视图集
# router.register("路由前缀",视图集类)
router.register("router_stu",views.StudentModelViewSet)

# 把生成的路由列表追加到urlpatterns
print( router.urls )
urlpatterns += router.urls


上面的代码就成功生成了路由地址[增/删/改/查一条/查多条的功能],但是不会自动我们在视图集自定义方法的路由。

所以我们如果也要给自定义方法生成路由,则需要进行action动作的声明。

2.2 视图集中附加action的声明

在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action装饰器。

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

action装饰器可以接收两个参数:

  • methods: 声明该action对应的请求方式,列表传递

  • detail

    : 声明该action的路径是否与单一资源对应,及是否是

    xxx/<pk>/action方法名/
    
    • True 表示路径格式是xxx/<pk>/action方法名/
    • False 表示路径格式是xxx/action方法名/

举例:

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 设置当前方法允许哪些http请求访问当前视图方法
    # detail 设置当前视图方法是否是操作一个数据
    # detail为True,表示路径名格式应该为 router_stu/{pk}/login/
    @action(methods=[''get''], detail=True)
    def login(self, request,pk):
        """登录"""
        ...

    # detail为False 表示路径名格式应该为 router_stu/get_new_5/
    @action(methods=[''put''], detail=False)
    def get_new_5(self, request):
        """获取最新添加的5个学生信息"""
        ...

由路由器自动为此视图集自定义action方法形成的路由会是如下内容:

^router_stu/get_new_5/$    name: router_stu-get_new_5
^router_stu/{pk}/login/$   name: router_stu-login

2.3 路由router形成URL的方式

1) SimpleRouter

2)DefaultRouter

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

Django - rest_framework

Django - rest_framework

一、 

  安装方式:

    pip install django

    pip install djangorestframework

    其他细节就不多说了

在setting中加入配置

INSTALLED_APPS = [
    ''django.contrib.admin'',
    ''django.contrib.auth'',
    ''django.contrib.contenttypes'',
    ''django.contrib.sessions'',
    ''django.contrib.messages'',
    ''django.contrib.staticfiles'',
    ''api.apps.ApiConfig'',  # 加入api
    ''rest_framework''  # 加入rest_framework
]
MIDDLEWARE = [
''django.middleware.security.SecurityMiddleware'',
''django.contrib.sessions.middleware.SessionMiddleware'',
''django.middleware.common.CommonMiddleware'',
''django.middleware.csrf.CsrfViewMiddleware'',
''django.contrib.auth.middleware.AuthenticationMiddleware'',
''django.contrib.messages.middleware.MessageMiddleware'',
''django.middleware.clickjacking.XFrameOptionsMiddleware'',
''api.cors.CORSMiddleware'' # 可以跨域请求
]
 
# 开restful接口
REST_FRAMEWORK = {
''DEFAULT_RENDERER_CLASSES'': [''rest_framework.renderers.JSONRenderer'', ''rest_framework.renderers.BrowsableAPIRenderer'',],
''DEFAULT_VERSIONING_CLASS'': ''rest_framework.versioning.URLPathVersioning'',
''ALLOWED_VERSIONS'': [''v1'', ''v2''], # 允许的版本
''VERSION_PARAM'': ''version'', # 参数
''DEFAULT_VERSION'': ''v1'', # 默认版本
}

 url.py

from django.conf.urls import url, include
from django.contrib import admin


urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r''^api/(?P<version>\w+)/'', include(''api.urls''))
]

api\urls.py   url路径

from django.conf.urls import url, include
from api.views import course

urlpatterns = [
    # 方式一
    # url(r''^course/'', course.CourseView.as_view()),
    # url(r''^course/(?P<pk>\d+)/'', course.CourseView.as_view())

    # 方式二
    url(r''^course/$'', course.CourseView.as_view({''get'': ''list''})),
    url(r''^course/(?P<pk>\d+)/$'', course.CourseView.as_view({''get'': ''retreice''})),

]

api\views\course.py     视图函数

from rest_framework.views import APIView
from rest_framework.response import Response
from api import models
from rest_framework import serializers


class CourseSerializer(serializers.ModelSerializer):
    level = serializers.CharField(source="get_level_display")  # 得到level数字对应的文字
    class Meta:
        model = models.Course
        fields = [''id'', ''title'', ''course_img'', ''level'']  # ''__all__'' 全部数据

# class CourseDetailSerializer(serializers.ModelSerializer):
#     class Meta:
#         model = models.CourseDetail
#         fields = ''__all__''
#         depth = 2  # 根据关联字段找到表序列化2层(0-10)

class CourseDetailSerializer(serializers.ModelSerializer):
    # 以下这3种方法只适合one2one/foreignkey/choice
    title = serializers.CharField(source=''course.title'')
    img = serializers.CharField(source=''course.course_img'')
    level = serializers.CharField(source=''course.get_level_display'')



    # many2many
    recommends = serializers.SerializerMethodField()
    chapter = serializers.SerializerMethodField()

    def get_recommends(self, obj):
        # 获取推荐的所有课程
        queryset = obj.recommend_courses.all()
        return [{''id'': row.id, ''title'': row.title} for row in queryset]

    def get_chapter(self, obj):
        # 获取推荐的所有章节
        queryset = obj.course.chapter_set.all()
        return [{''id'': row.id, ''num'': row.num, ''name'': row.name} for row in queryset]


    class Meta:
        model = models.CourseDetail
        fields = [''why'', ''title'', ''img'', ''level'', ''course'', ''slogon'', ''recommends'', ''chapter'']  # 指定数据
        depth = 2

#  方法一
# class CourseView(APIView):
#     def get(self, request, *args, **kwargs):
        # ret = {
        #     ''code'': 1000,
        #     ''data'': [
        #         {"id": 1, "title": ''python全栈''},
        #         {"id": 2, ''title'': ''Linux运维''},
        #         {"id": 3, ''title'': ''金融分析''}
        #     ]
        # }
        # return Response(ret)
# 方法一
        # ret = {''code'': 1000, ''data'': None}
        # try:
        #     pk = kwargs.get(''pk'')
        #     if pk:
        #         obj = models.Course.objects.filter(id=pk).first()
        #         # 序列化
        #         ser = CourseSerializer(instance=obj, many=False)
        #     else:
        #         queryset = models.Course.objects.all()
        #         # 序列化
        #         ser = CourseSerializer(instance=queryset, many=True)
        #     ret[''data''] = ser.data
        # except Exception as e:
        #     ret[''code''] = 1001
        #     ret[''data''] = ''获取失败''
        # return Response(ret)

# 方法二
# views
# APIView
# GenericAPIView

from rest_framework.viewsets import GenericViewSet, ViewSetMixin


class CourseView(ViewSetMixin, APIView):
    def list(self, request, *args, **kwargs):
        """
        课程列表接口
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = {''code'': 1000, ''data'': None}
        try:
            queryset = models.Course.objects.all()
            ser = CourseSerializer(instance=queryset, many=True)
            ret[''data''] = ser.data
        except Exception as e:
            ret[''code''] = 1001
            ret[''error''] = ''获取失败''
        return Response(ret)

    def retreice(self, request, *args, **kwargs):
        """
        个人信息接口
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        ret = {''code'': 1000, ''data'': None}
        try:
            # 课程ID
            pk = kwargs.get(''pk'')
            # obj = models.Course.objects.filter(id=pk).first()  # 通过Course查询ID为pk的信息
            # ser = CourseSerializer(instance=obj, many=False)  # 序列化obj信息
            obj = models.CourseDetail.objects.filter(course_id=pk).first()
            ser = CourseDetailSerializer(instance=obj, many=False)
            ret[''data''] = ser.data
        except Exception as e:
            ret[''code''] = 1001
            ret[''error''] = ''获取失败''
        return Response(ret)

api/serializers/course.py    归类要查找的数据

from api import models
from rest_framework import serializers

class CourseSerializer(serializers.ModelSerializer):
    """
    课程信息的类
    """
    level = serializers.CharField(source="get_level_display")  # 得到level数字对应的文字
    class Meta:
        model = models.Course
        fields = [''id'', ''title'', ''course_img'', ''level'']  # ''__all__'' 全部数据

# class CourseDetailSerializer(serializers.ModelSerializer):
#     class Meta:
#         model = models.CourseDetail
#         fields = ''__all__''
#         depth = 2  # 根据关联字段找到表序列化2层(0-10)

class CourseDetailSerializer(serializers.ModelSerializer):
    """
    课程详情的类
    """
    # 以下这3种方法只适合one2one/foreignkey/choice
    title = serializers.CharField(source=''course.title'')
    img = serializers.CharField(source=''course.course_img'')
    level = serializers.CharField(source=''course.get_level_display'')



    # many2many
    recommends = serializers.SerializerMethodField()
    chapter = serializers.SerializerMethodField()

    def get_recommends(self, obj):
        # 获取推荐的所有课程
        queryset = obj.recommend_courses.all()
        return [{''id'': row.id, ''title'': row.title} for row in queryset]

    def get_chapter(self, obj):
        # 获取推荐的所有章节
        queryset = obj.course.chapter_set.all()
        return [{''id'': row.id, ''num'': row.num, ''name'': row.name} for row in queryset]


    class Meta:
        model = models.CourseDetail
        fields = [''why'', ''title'', ''img'', ''level'', ''course'', ''slogon'', ''recommends'', ''chapter'']  # 指定数据
        depth = 2

api/cors.py   设置跨域请求

from django.utils.deprecation import MiddlewareMixin

class CORSMiddleware(MiddlewareMixin):
    def process_response(self,request,response):
        # 允许你的域名来访问
        response[''Access-Control-Allow-Origin''] = "*"
        # 允许你携带 Content-Type 请求头 不能写*
        response[''Access-Control-Allow-Headers''] = ''Content-Type''
        # 允许你发送 DELETE PUT请求
        response[''Access-Control-Allow-Methods''] = ''DELETE,PUT''
        return response

API接口

课程接口:
    127.0.0.1:8000/api/v1/course/
    127.0.0.1:8000/api/v2/course/
单个课程接口:
    127.0.0.1:8000/api/v1/course/1
    127.0.0.1:8000/api/v2/course/1

- 路由 as_view 是否添加参数,取决于视图继承的类
- 序列化
    - depth
    - source
    - 自定义method

 

 

rest_frameword详情

Django REST Framework 批量更新 rest_framework_extensions

Django REST Framework 批量更新 rest_framework_extensions

Django REST framework 是一套基于 Django 框架编写 RESTful 风格 API 的组件。

 其中 mixins 配合 viewsets 能极其方便简化对数据的增删改查,

但本身并没有对数据的批量更新删除,利用 rest_framework_extensions 扩展包可以轻松帮我们实现这些功能。

安装使用

pip install rest_framework_extensions

 

views.py

在视图类中继承 ListUpdateModelMixin

1 from rest_framework_extensions.mixins import ListUpdateModelMixin
2 class ShoppingCartViewSet(ListUpdateModelMixin, viewsets.ModelViewSet):
3     pass

 

settings.py

1 CORS_ALLOW_HEADERS = [''*'']  # 允许的请求头
2 CORS_ORIGIN_ALLOW_ALL = True  # 允许跨域
3 
4 REST_FRAMEWORK_EXTENSIONS = {
5     ''DEFAULT_BULK_OPERATION_HEADER_NAME'': None
6 }

使用浏览器本地测试,在请求头加上:X-BULK-OPERATION: true

使用 patch 方式请求测试成功,状态码 204,不会返回任何东西

 

使用 PUT 方法批量更新

以上在浏览器使用正常,但发现在微信小程序中并不支持 patch 方法,只能用 put 方法。要么重新再写一个 put 接口,要么更改源码。

ctrl 点击查看 ListUpdateModelMixin 码源,果真只有 put 方法。怎么办?

把 patch 方法复制粘贴一份,改名为 put 即可,同样测试成功。

\Lib\site-packages\rest_framework_extensions\bulk_operations\mixins.py

 

对于以上修改。对于没有使用虚拟环境的同学,个人建议不要直接修改源码,一定要把整个包拷到项目目录下再修改。

在项目目录下新建 extra_apps 文件夹,用来存放要修改的第三方包。

再在 settings.py 中添加以下。优先从 extra_apps 文件夹导包。

import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, ''extra_apps''))

 微信小程序测试正常

 

 

 

 

Django Rest Framework源码剖析(八)-----视图与路由

Django Rest Framework源码剖析(八)-----视图与路由

一、简介

django rest framework 给我们带来了很多组件,除了认证、权限、序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使用者而言不同的视图具有不同的功能,这样我们可以根据需求定制自己视图。以下是官网传送门:http://www.django-rest-framework.org/api-guide/views/

在之前的文章中,由于参杂了权限、认证等(如果不了解请看博客的以前的文章),但在本章中基本可以不使用,所进使用以下简单模型进行说明:

settings中注册django rest framework

INSTALLED_APPS = [
    ''django.contrib.admin'',
    ''django.contrib.auth'',
    ''django.contrib.contenttypes'',
    ''django.contrib.sessions'',
    ''django.contrib.messages'',
    ''django.contrib.staticfiles'',
    ''app01.apps.App01Config'',
    ''rest_framework'',
]

models.py

from django.db import models

# Create your models here.

class UserInfo(models.Model):
    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r''^api/v1/users'', views.UerView.as_view()),  

]
二、各类视图使用

1.APIView

该视图是最基本的视图,之前的章节中已经介绍如何使用,参考:Django Rest Framework源码剖析(一)-----认证

2.GenericAPIView

该视图为我们封装一些静态字段,用于调用其他组件,示例(解释请看注解):

from django.shortcuts import render

# Create your views here.

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework import serializers
from rest_framework import pagination
from app01 import models

class Userserializer(serializers.ModelSerializer):   #序列化类定义,前面章节已经介绍
    class Meta:
        model = models.UserInfo
        fields = ''__all__''  #序列化字段

class Mypagination(pagination.PageNumberPagination):  # 分页类定义,前面章节也已经介绍
    """自定义分页"""
    page_size=2  #默认每页显示个数配置
    page_query_param = ''p'' # 页面传参的key,默认是page
    page_size_query_param=''size''  # 指定每页显示个数参数
    max_page_size=4 # 每页最多显示个数配置,使用以上配置,可以支持每页可显示2~4条数据

class UerView(GenericAPIView):            #视图
    queryset = models.UserInfo.objects.all()  #数据的queryset
    serializer_class = Userserializer         # 序列化类使用
    permission_classes = []                   # 权限认证,这里不做认证,前面章节也有介绍权限
    pagination_class = Mypagination #分页类,前面章节也已经介绍
    def get(self,*args,**kwargs):
        roles=self.get_queryset()  # 获取queryset,实际取queryset,也就是models.UserInfo.objects.all()
        page_res=self.paginate_queryset(queryset=roles)  #分页结果
        res=self.get_serializer(instance=page_res,many=True)  #序列化
        return Response(res.data)  #返回结果

访问http://127.0.0.1:8000/api/v1/users,查看结果如下:

3.GenericViewSet

该视图类需要和路由配合使用,修改后的urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r''^admin/'', admin.site.urls),
   # url(r''^api/v1/users'', views.UserView.as_view()),
    url(r''^api/v2/users'', views.UserView.as_view({''get'':''show'',''post'':''create''})), #重写as_view方法,并对请求方法进行映射

]

views.py

from django.shortcuts import render

# Create your views here.

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework import serializers
from rest_framework import pagination
from rest_framework.viewsets import GenericViewSet
from app01 import models

class Userserializer(serializers.ModelSerializer):   #序列化类定义,前面章节已经介绍
    class Meta:
        model = models.UserInfo
        fields = ''__all__''  #序列化字段

class Mypagination(pagination.PageNumberPagination):  # 分页类定义,前面章节也已经介绍
    """自定义分页"""
    page_size=2  #默认每页显示个数配置
    page_query_param = ''p'' # 页面传参的key,默认是page
    page_size_query_param=''size''  # 指定每页显示个数参数
    max_page_size=4 # 每页最多显示个数配置,使用以上配置,可以支持每页可显示2~4条数据

class UserView(GenericViewSet):            #视图
    queryset = models.UserInfo.objects.all()  #数据的queryset
    serializer_class = Userserializer         # 序列化类使用
    permission_classes = []                   # 权限认证,这里不做认证,前面章节也有介绍权限
    pagination_class = Mypagination #分页类,前面章节也已经介绍
    def show(self,*args,**kwargs):  #与url中映射的方法名称相同
        roles=self.get_queryset()  # 获取queryset,实际取queryset,也就是models.UserInfo.objects.all()
        page_res=self.paginate_queryset(queryset=roles)  #分页结果
        res=self.get_serializer(instance=page_res,many=True)  #序列化
        return Response(res.data)  #返回结果

    def create(self,*args,**kwargs):
        pass

访问http://127.0.0.1:8000/api/v2/users,结果和上面结果一样如图:

4.ModelViewSet

modelViewSet继承了mixins.CreateModelMixin、mixins.RetrieveModelMixin、mixins.UpdateModelMixin、mixins.DestroyModelMixin、mixins.ListModelMixin、GenericViewSet所以它有很父类的所有功能,而这些父类分别提供创建、获取、更新、删除等方法,所以我们不需要写增删该查,视图已经帮我实现了,示例:

此时路由urls.py

"""resetful URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r''^$'', views.home, name=''home'')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r''^$'', Home.as_view(), name=''home'')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r''^blog/'', include(''blog.urls''))
"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r''^api/v1/users$'', views.UserView.as_view({''get'':''list'',''post'':''create''})),
    #路由,retrieve、delete、create、update、partial_update,在UserView的父类全部实现,其中pk参数必须携带,想当于key,类可操作
    url(r''^api/v1/users/(?P<pk>\d+)$'', views.UserView.as_view({''get'':''retrieve'',''delete'':''destroy'',''post'':''create'',''put'':''update'',''patch'':''partial_update''})),

]

视图views.py

from django.shortcuts import render

# Create your views here.

from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework import serializers
from rest_framework import pagination
from rest_framework.viewsets import ModelViewSet
from app01 import models

class Userserializer(serializers.ModelSerializer):   #序列化类定义,前面章节已经介绍
    class Meta:
        model = models.UserInfo
        fields = ''__all__''  #序列化字段

class Mypagination(pagination.PageNumberPagination):  # 分页类定义,前面章节也已经介绍
    """自定义分页"""
    page_size=2  #默认每页显示个数配置
    page_query_param = ''p'' # 页面传参的key,默认是page
    page_size_query_param=''size''  # 指定每页显示个数参数
    max_page_size=4 # 每页最多显示个数配置,使用以上配置,可以支持每页可显示2~4条数据

class UserView(ModelViewSet):            #视图
    queryset = models.UserInfo.objects.all()  #数据的queryset
    serializer_class = Userserializer         # 序列化类使用
    permission_classes = []                   # 权限认证,这里不做认证,前面章节也有介绍权限
    pagination_class = Mypagination #分页类,前面章节也已经介绍

访问http://127.0.0.1:8000/api/v1/users/1,查看多增删改。

 

三、路由

前面的示例中已经用到类路由的功能,在这里介绍下router使用方法,一般情况,我们写路由时候对于增删该查可能写多个路由,其实我们可以借助DRF的router自动帮我们生成路由,示例:

"""resetful URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r''^$'', views.home, name=''home'')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r''^$'', Home.as_view(), name=''home'')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r''^blog/'', include(''blog.urls''))
"""
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
from rest_framework import routers
router=routers.DefaultRouter() #是列化router类
router.register(r''userinfo$'',views.UserView)  #注册一个url,后续会生成我们想要的个url

##生成的url


urlpatterns = [
    url(r''^admin/'', admin.site.urls),
    url(r''^api/v1/users$'', views.UserView.as_view({''get'':''list'',''post'':''create''})),
    #路由,retrieve、delete、create、update、partial_update,在UserView的父类全部实现
    url(r''^api/v1/users/(?P<pk>\d+)$'', views.UserView.as_view({''get'':''retrieve'',''delete'':''destroy'',''post'':''create'',''put'':''update'',''patch'':''partial_update''})),
    url(r''^api/v1'',include(router.urls)),#将生成的url加入到路由中
]

此时我们找一个不存在的页面查看路由:

四、源码剖析

需要知道的知识点:

  • 子类拥有父类的所有功能

由于视图类较多,所以我们对源码的剖析就不一一分析了,这里以一个常用的GenericAPIView为例子分析下源码。首先从继承角度来说,GenericAPIView继承了APIView,而APIView又继承了View,而APIView是对django原生的View类的dispatch方法进行了重写(在认证的篇章已经说明),所以GenericAPIView具有APIView和View的所有功能,下面是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           # queryset数据配置,这就是为什么我们示例中的queryset = models.UserInfo.objects.all()
    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''       # 浏览条数据使用的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):               # 获取queryset方法
        """
        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__
        )

        queryset = self.queryset             #  将配置querset 转化为类属性
        if isinstance(queryset, QuerySet):   #  判断配置queryset是不是QuerySet类型
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()        # 返回所有该Queryset类型的数据
        return queryset

    def get_object(self):                   # 获取单个的对象,需要在url中配置pk,其中pk的参数一般是ID
        """
        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.
        """
        serializer_class = self.get_serializer_class()  # 执行get_serializer_class用于获取序列化类
        kwargs[''context''] = self.get_serializer_context() 
        return serializer_class(*args, **kwargs)  # 序列化数据,返回序列化以后的结果

    def get_serializer_class(self):  # 获取序列化类 ,也就是serialzier_class配置
        """
        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): # 过滤,参数为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     #不存在返回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)
五、总结

使用建议:

1.对于简单的数据模型来说,建议使用modelViewSet,因为它已经帮我们写好了增删该查接口。

2.对于复杂的数据模型,比如涉及多个表操作或有事务要求时,此时更建议使用APIView或者GenericAPIView,这样可以根据需求我们自己写增删该查接口。

 

Django REST_framework 路由控制

Django REST_framework 路由控制

一 自定义路由(原始方式)

urls.py 文件
from
django.conf.urls import url from app01 import views urlpatterns = [ url(r''^books$'', views.BookView.as_view()),
url(r''^books/(?P<pk>\d+)$'', views.BookDetailView.as_view()) ]

 

views.py 文件
class
BookView(APIView): def get(self, request): book_list = models.Book.objects.all() bs = BookSerializers(book_list, many=True) return Response(bs.data) def post(self, request): # 添加一条数据 print(request.data) bs=BookSerializers(data=request.data) if bs.is_valid(): bs.save() # 生成记录 return Response(bs.data) else: return Response(bs.errors) class BookDetailView(APIView): def get(self,request,pk): book_obj=models.Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,many=False) return Response(bs.data) def put(self,request,pk): book_obj = models.Book.objects.filter(pk=pk).first() bs=BookSerializers(data=request.data,instance=book_obj) if bs.is_valid(): bs.save() # update return Response(bs.data) else: return Response(bs.errors) def delete(self,request,pk): models.Book.objects.filter(pk=pk).delete() return Response("")

 

二 半自动路由(视图类继承ModelViewSet)

urls.py 文件
from django.conf.urls import url
from app01 import views

urlpatterns = [
url(r''^publish/$'', views.PublishView.as_view({''get'': ''list'', ''post'': ''create''}))
url(r''^publish/(?<pk>\d+)/$'', views.PublishView.as_view({''get'': ''retrieve'', ''put'': ''update'', ''delete'': ''destory''}))
]
views.py 文件
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
queryset = models.Publish.objects.all()
serializer_class = serializers.PublishSerializer

 

三 全自动路由(自动生成路由)

urls.py 文件
from django.conf.urls import url, include
from app01 import views
from rest_framework import routers
router = routers.DefaultRouter()
# 参数 该视图集的路由前缀 视图集 路由名称的前缀
router.register(''publish'', views.PublishView, base_name=''good'')
urlpatterns = [
  url(r'''', include(router.urls))
]

views.py 文件
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
queryset = models.Publish.objects.all()
serializer_class = serializers.PublishSerializer

 router = routers.DefaultRouter()

生成四条路由

router = routers.SimpleRouter()

生成两条路由

 

我们今天的关于Rest_Framework的视图与路由rest framework的分享已经告一段落,感谢您的关注,如果您想了解更多关于Django - rest_framework、Django REST Framework 批量更新 rest_framework_extensions、Django Rest Framework源码剖析(八)-----视图与路由、Django REST_framework 路由控制的相关信息,请在本站查询。

本文标签: