• DRF视图和路由


    APIView :

    在django中写CBV的时候是继承View, rest_framework继承的是APIView, 这两种具体有什么不同呢?

    urlpatterns = [
        url(r'^book$', BookView.as_view()),
        url(r'^book/(?P<id>d+)$', BookEditView.as_view()),
    ]

    无论是View还是APIView最开始调用的都是as_view()方法, 看源码:

      可以看到, APIView继承了View, 并且执行了View中的as_view()方法, 最后把view返回, 用csrf_exempt()方法包裹后去掉了csrf的认证.

    而在View中的as_view()方法如下:

      在View中的as_view方法返回了view函数, 而view函数执行了self.dispatch()方法, 但是这里的dispatch方法应该是APIView中的.

     再去initialize_request中看下把什么赋值给了request, 并且赋值给了self.request, 也就是在视图中用的request.xxx到底是什么?

      可以看到, 这个方法返回的是Request这个类的实例对象, 而这个Request类中的第一个参数request, 使我们在django中使用的request.

      可以看到, 这个Request类把原来的request赋值给了self._request, 也就是说_request就是我们原先的request, 新的request使我们这个Request类.

    那继承APIView之后请求来的数据都在哪呢?

    当我们使用了rest_framework框架之后, 我们的request是重新封装的Request类.

    request.query_params 存放的是我们get请求的参数.

    request.data 存放的是我们所有的数据, 包括post请求的以及put, patch请求.

    相比原来的django的request, 我们现在的request更加精简, 清晰.


    封装代码 :

    原代码(封装之前) :

    from django.conf.urls import url
    from SerDemo import views
    
    urlpatterns = [
        # 第一二版本
        # url(r'^book/$', views.BookView.as_view()),
        # url(r'^book/(?P<edit_id>d+)', views.BookEditView.as_view()),
    
    ]
    url
    from rest_framework import serializers
    from app01 import models
    
    
    class PublisherSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        title = serializers.CharField(max_length=32)
    
    
    class AuthorSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        name = serializers.CharField(max_length=32)
    
    
    # 一个判断某一个字段是否含有敏感信息的函数, 直接调用即可
    def my_validate(value):
        if "敏感信息" in value.lower():
            raise serializers.ValidationError("存在敏感词汇!!!")
    
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)  # required 为False时, 反序列化不做校验
        title = serializers.CharField(max_length=32, validators=[my_validate])
        pub_time = serializers.DateField()
        category = serializers.CharField(source="get_category_display", read_only=True)
        # 自定义一个字段只用来反序列化接收使用
        post_category = serializers.IntegerField(write_only=True)
    
        publisher = PublisherSerializer(read_only=True)
        publisher_id = serializers.IntegerField(write_only=True)
        # 多对多有many参数
        author = AuthorSerializer(many=True, read_only=True)
        author_list = serializers.ListField(write_only=True)
    
        # 新增数据要重写的create方法
        def create(self, validated_data):
            # validated_data是验证通过的数据
            # 通过ORM操作给Book表增加数据
            # 添加除多对多字段的所有字段
            book_obj = models.Book.objects.create(
                title=validated_data["title"],
                pub_time=validated_data["pub_time"],
                category=validated_data["post_category"],
                publisher_id=validated_data["publisher_id"],
            )
            # 添加多对多字段
            book_obj.author.add(*validated_data["author_list"])
            return book_obj
    
        # 更新数据要重写update方法
        def update(self, instance, validated_data):
            # instance 是要更新的对象
            # 对除多对多字段以外的字段进行更新, 并设置当前已存在的数据为默认值
            instance.title = validated_data.get("title", instance.title)
            instance.pub_time = validated_data.get("pub_time", instance.pub_time)
            instance.category = validated_data.get("post_category", instance.category)
            instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
            # 判断前端传过来的数据是否含有author_list字段, 如果有则更新, 没有就不变动
            if validated_data.get("author_list"):
                instance.author.set(validated_data["author_list"])
            instance.save()
            return instance
    
    
    # 对前端传过来的数据进行条件控制
    def validate(self, attrs):
        # 相当于钩子函数
        # attrs是一个字典, 含有传过来的所有字段
        if "python" in attrs["title"].lower() and attrs["post_category"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分类或标题不匹配")
    序列化器
    from rest_framework.views import APIView
    from app01 import models
    from .serializers import BookSerializer
    from rest_framework.response import Response
    
    from rest_framework.viewsets import ViewSetMixin
    
    
    
    # Create your views here.
    
    
    # 版本一
    class BookView(APIView):
        def get(self, request):
            book_queryset = models.Book.objects.all()
            # 用序列化器进行序列化
            ser_obj = BookSerializer(book_queryset, many=True)
    
            return Response(ser_obj.data)
    
        def post(self, request):
            # 接收前端传过来的数据
            book_obj = request.data
            # 对前端传过来的数据使用自定义序列化方法进行校验(是否合法等)
            ser_obj = BookSerializer(data=book_obj)
            # 如果校验通过做些什么
            if ser_obj.is_valid():
                ser_obj.save()
                # validated_data是校验通过之后的数据
                return Response(ser_obj.validated_data)
            # 验证不通过返回错误信息
            return Response(ser_obj.errors)
    
    
    class BookEditView(APIView):
        def get(self, request, edit_id):
            book_obj = models.Book.objects.filter(id=edit_id).first()
            ser_obj = BookSerializer(book_obj)
            return Response(ser_obj.data)
    
        def put(self, request, edit_id):
            book_obj = models.Book.objects.filter(id=edit_id).first()
            ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.validated_data)
            return Response(ser_obj.errors)
    
        def delete(self, request, edit_id):
            book_obj = models.Book.objects.filter(id=edit_id).first()
            if not book_obj:
                return Response("删除对象不存在!")
            book_obj.delete()
            return Response("删除成功了呢!")
    view.py

    第一次封装 :

    from django.conf.urls import url
    from SerDemo import views
    
    urlpatterns = [
        # 第一二版本
        # url(r'^book/$', views.BookView.as_view()),
        # url(r'^book/(?P<edit_id>d+)', views.BookEditView.as_view()),
    
    
    ]
    url
    class GenericAPIView(APIView):
        """
        定义一个公共的类, 用来获取需要的资源
        """
        # 设置默认操作的数据为空
        queryset = None
        # 设置需要使用的序列化器为空
        serializer_class = None
    
        def get_queryset(self):
            """
            定义一个获取需要操作的数据的函数
            子类函数直接调用即可
            子类中调用此方法, 返回值中的self指的是调用此方法的子类的实力化对象
            queryset属性在本类中是None, 每一个继承此类的子类中都会重写queryset和serializer_class方法
            然后子类中执行此类中的方法是去执行子类对应的属性.
            :return:
            """
            return self.queryset.all()
    
        def get_serializer_class(self, *args, **kwargs):
            return self.serializer_class(*args, **kwargs)
    
    
    class ListModelMixin:
        """
        定义一个展示类,将展示方法统一写成一个方法
        """
    
        def list(self, request):
            queryset = self.get_queryset()
            ser_obj = self.get_serializer_class(queryset, many=True)
            return Response(ser_obj.data)
    
    
    class CreateModeMixin:
        """
        新增的视图类
        """
    
        def create(self, request):
            ser_obj = self.get_serializer_class(data=request.data)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.validated_data)
            return Response(ser_obj.errors)
    
    
    class EditModeMixin:
        """
        编辑的视图类
        """
    
        def retrieve(self, request, id):
            obj = self.get_queryset().filter(id=id).first()
            ser_obj = self.get_serializer_class(obj)
            return Response(ser_obj.data)
    
    
    class UpdateModeMixin:
        """
        更新的视图类
        """
    
        def update(self, request, id):
            obj = self.get_queryset().filter(id=id).first()
            ser_obj = self.get_serializer_class(instance=obj, data=request.data, partial=True)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.validated_data)
            return Response(ser_obj.errors)
    
    
    class DeleteModeMixin:
        """
        删除的视图类
        """
    
        def destroy(self, request, id):
            obj = self.get_queryset().filter(id=id).first()
            if not obj:
                return Response("删除的对象不存在!")
            obj.delete()
            return Response("删除了呢")
    
    
    class ListCreateAPIview(GenericAPIView, ListModelMixin, CreateModeMixin):
        pass
    
    
    class OperationAPIview(GenericAPIView, EditModeMixin, UpdateModeMixin, DeleteModeMixin):
        pass
    
    
    class BookView(ListCreateAPIview):
        # 定义queryset属性时, DRF方法内部和关键字重名, 内部会识别并将此属性做缓存
        # 换个名字DRF不识别属性, 不做缓存, 也就不需要.all()
        queryset = models.Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    
    class BookEditView(OperationAPIview):
        queryset = models.Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request, edit_id):
            return self.retrieve(request, edit_id)
    
        def put(self, request, edit_id):
            return self.update(request, edit_id)
    
        def delete(self, request, edit_id):
            return self.destroy(request, edit_id)
    view.py

      我们封装的GenericAPIView, , 包括封装的每个方法的类, 其实框架都帮我们封装好了,

      我们可以继承这个二类, 来实现上面的视图.

    其中框架还给我们提供了一个路由传参的方法:

      actioon这个默认参数其实就是接收路由参数的参数.

    再次封装 :

    from SerDemo import views
    
    urlpatterns = [
    
        # 第三版
        # url(r'^book/$', views.BookModelView.as_view({"get": "list", "post": "create"})),
        # url(r'^book/(?P<pk>d+)', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
    ]
    url
    from rest_framework.viewsets import ViewSetMixin
    
    
    # 重写了源码中的as_view()方法, 是as_view方法可以传参数
    # 在执行dispatch()方法之前
    class ModelViewSet(ViewSetMixin, ListCreateAPIview, OperationAPIview):
        pass
    
    
    class BookModelView(viewsets.ModelViewSet):
        queryset = models.Book.objects.all()
        serializer_class = BookSerializer
    view.py

      这样我们的视图只要写两行就可以了

      其实我们所写的所有视图, 框架都帮我们封装好了.

      注意 :

        应框架封装的视图, url上的那个关键字参数要用pk, 系统默认的


    继承顺序图 :


    DRF路由 :

    # 最终版
    # 帮助我们生成带参数的路由
    from rest_framework.routers import DefaultRouter
    # 实例化DefaultRouter对象
    router = DefaultRouter()
    # 注册我们的路由以及视图
    router.register(r"book", views.BookModelView)
    
    
    urlpatterns = [
    
    ]
    
    urlpatterns += router.urls

    可以看到, 通过框架可以把路由视图都变得非常简单, 但是需要自定制的时候还是需要自己用APIView写, 当不需要那么多路由时候, 也不需药使用这种路由注册.

  • 相关阅读:
    前端开发 vue,angular,react框架对比1
    前端开发 Angular
    前端开发 Vue Vue.js和Node.js的关系
    net技术
    net技术方案
    软件工程项目费用计算
    前端开发 Vue -4promise解读2
    前端开发 Vue -4promise解读1
    mybatis与hibernate区别
    struts2和springmvc的区别
  • 原文地址:https://www.cnblogs.com/dong-/p/9974646.html
Copyright © 2020-2023  润新知