• Django REST framework 1


    Django REST framework

    Django REST framework官方文档:点击  中文文档:点击

    1. 安装djangorestframework:pip3 install djangorestframework (pip3 list 查看详情和版本信息)
    2. 注册rest_framework(settings.py)
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01',
        'rest_framework',
    ]
    settings.py

    DRF序列化

    做前后端分离的项目,前后端交互一般都选择JSON数据格式,JSON是一个轻量级的数据交互格式。后端给前端数据的时候都要转成json格式,那就需要对从数据库拿到的数据进行序列化。

    要用DRF的序列化,就要遵循人家框架的一些标准

    1. 声明序列化类(app01目录下新建serializers.py文件)
    2. Django CBV继承类是View,现在DRF要用APIView
    3. Django中返回的时候用HTTPResponse,JsonResponse,render ,DRF用Response
    4. DRF反序列化

      • 当前端给后端发post的请求的时候,前端给后端传过来的数据,要进行一些校验然后保存到数据库。
      • 这些校验以及保存工作,DRF的Serializer也提供了一些方法了,
      • 首先要写反序列化用的一些字段,有些字段要跟序列化区分开,
      • Serializer提供了.is_valid()和.save()方法

    一、Serializer

    1、声明序列化类(app01中serializers.py)

    from rest_framework import serializers
    from .models import Book
    
    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)
    
    
    book_obj = {
            "title": "Alex的使用教程",
            "w_category": 1,
            "pub_time": "2018-10-09",
            "publisher_id": 1,
            "author_list": [1, 2]
        }
    
    
    data = {
        "title": "Alex的使用教程2"
    }
    
    #3.1 验证器 validators  定义函数写验证规则
    def my_validate(value):
        if "敏感信息" in value.lower():
            raise serializers.ValidationError("不能含有敏感信息")
        else:
            return value
    
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)  #required=False  不需要校验
        title = serializers.CharField(max_length=32, validators=[my_validate])  #3.2 验证器  validators用自定义的验证方法,比validate_title权重高
        CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
        category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)  #choices
        w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
        pub_time = serializers.DateField()
        #外键关系的序列化
        publisher = PublisherSerializer(read_only=True)  #read_only=True只序列化的时候用
        publisher_id = serializers.IntegerField(write_only=True)  #ForeignKey   #write_only=True只反序列化的时候用
        #ManyToMany的序列化
        author = AuthorSerializer(many=True, read_only=True)   #ManyToManyField
        author_list = serializers.ListField(write_only=True)
        #新建
        def create(self, validated_data):
            book = Book.objects.create(title=validated_data["title"], category=validated_data["w_category"],
                                    pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"])
            book.author.add(*validated_data["author_list"])
            return book
        #修改
        def update(self, instance, validated_data):
            instance.title = validated_data.get("title", instance.title)
            instance.category = validated_data.get("w_category", instance.w_category)
            instance.pub_time = validated_data.get("pub_time", instance.pub_time)
            instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
            if validated_data.get("author_list"):
                instance.author.set(validated_data["author_list"])
            instance.save()
            return instance
        #验证
        #1、单个字段的验证
        def validate_title(self, value):
            if "python" not in value.lower():
                raise serializers.ValidationError("标题必须含有python")
            return value
        #2、多个字段的验证
        def validate(self, attrs):
            if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
                return attrs
            else:
                raise serializers.ValidationError("分类以及标题不符合要求")
        #3、验证器 validators
    app01中serializers.py

    2、用声明的序列化类去序列化(app01中views.py)

    from .models import Book
    
    from rest_framework.views import APIView
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.response import Response
    from .serializers import BookSerializer
    
    
    
    class BookView(APIView):
        def get(self, request):
            # book_obj = Book.objects.first()
            # ret = BookSerializer(book_obj)
            book_list = Book.objects.all()
            ret = BookSerializer(book_list, many=True)  #多个ManyToManyField
            return Response(ret.data)
    
    
        def post(self, request):
            print(request.data)
            serializer = BookSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            else:
                return Response(serializer.errors)
    
    class BookEditView(APIView):
        def get(self, request, id):
            book_obj = Book.objects.filter(id=id).first()
            ret = BookSerializer(book_obj)
            return Response(ret.data)
    
        def put(self, request, id):
            book_obj = Book.objects.filter(id=id).first()
            serializer = BookSerializer(book_obj, data=request.data, partial=True) #partial=True允许部分进行更新
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            else:
                return Response(serializer.errors)
    
        def delete(self, request, id):
            book_obj = Book.objects.filter(id=id).first()
            book_obj.delete()
            return Response("")
    app01中views.py

    二、ModelSerializer

    • 跟模型紧密相关的序列化器
    • 根据模型自动生成一组字段
    • 默认实现了.update()以及.create()方法

    1、定义一个ModelSerializer序列化器(app01中serializers.py)

    from rest_framework import serializers
    from .models import Book
    
    # 验证器 validators  定义函数写验证规则
    def my_validate(value):
        if "敏感信息" in value.lower():
            raise serializers.ValidationError("不能含有敏感信息")
        else:
            return value
    
    
    class BookSerializer(serializers.ModelSerializer):
        #category = serializers.CharField(source="get_category_display",read_only=True)   #自定制
        category_display = serializers.SerializerMethodField(read_only=True)  # SerializerMethodField
        publisher_info = serializers.SerializerMethodField(read_only=True)
        authors = serializers.SerializerMethodField(read_only=True)
    
        def get_category_display(self, obj):
            return obj.get_category_display()
    
        def get_authors(self, obj):
            # obj是当前序列化的book对象
            #外键关联的对象有很多字段是用不到的~都传给前端会有数据冗余~就需要去定制序列化外键对象的哪些字段~~
            authors_query_set = obj.author.all()
            return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set]
    
        def get_publisher_info(self, obj):
            # obj 是我们序列化的每个Book对象
            # 外键关联的对象有很多字段是用不到的~都传给前端会有数据冗余~就需要去定制序列化外键对象的哪些字段~~
            publisher_obj = obj.publisher
            return {"id": publisher_obj.id, "title": publisher_obj.title}
    
        class Meta:
            model = Book
            # fields = ["id", "title", "pub_time"]
            # exclude = ["user"]
            # 包含某些字段 排除某些字段
            fields = "__all__"
            # depth = 1  #depth 代表找嵌套关系的第1层
            #注意:当序列化类MATE中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读
            #read_only_fields = ["id", "category_display", "publisher_info", "authors"]
            extra_kwargs = {"category": {"write_only": True}, "publisher": {"write_only": True},
                            "author": {"write_only": True},"title": {"validators": [my_validate,]}}
    app01中serializers.py
    from rest_framework import serializers
    from api import models
    
    
    class CourseModelSerializer(serializers.ModelSerializer):
        # price = serializers.SerializerMethodField()
        # learn_num = serializers.SerializerMethodField()
        learn_num = serializers.IntegerField(source='order_details.count')
        course_detail_id = serializers.IntegerField(source='coursedetail.id')
    
        # def get_price(self, obj):
        #     # 把所有课程永久有效的价格拿出来
        #     price_obj = obj.price_policy.all().filter(valid_period=999).first()
        #     return price_obj.price
    
        # def get_learn_num(self, obj):
        #     return obj.order_details.count()
    
        # 修改序列化结果的终极方法
        def to_representation(self, instance):
            # 调用父类的同名方法把序列化的结果拿到
            data = super().to_representation(instance)
            # 针对序列化的结果做一些自定制操作
            # 判断当前这个课程是否有永久有效的价格
            price_obj = instance.price_policy.all().filter(valid_period=999).first()
            if price_obj:
                # 有永久有效的价格
                data['has_price'] = True
                data['price'] = price_obj.price
            else:
                # 没有永久有效的价格策略
                data['has_price'] = False
            return data
    
        class Meta:
            model = models.Course
            fields = '__all__'
    
    
    class CourseCategoryModelSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.CourseCategory
            fields = '__all__'
    to_representation

    三、Serializer和ModelSerializer的区别

    四、JsonResponse和Django序列化

    #app01中models.py
    from django.db import models
    __all__ = ["Book", "Publisher", "Author"]
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32, verbose_name="图书名称")
        CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
        category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别")
        pub_time = models.DateField(verbose_name="图书的出版日期")
    
        publisher = models.ForeignKey(to="Publisher", on_delete=None)
        author = models.ManyToManyField(to="Author")
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name_plural = "01-图书表"
            db_table = verbose_name_plural
    
    
    class Publisher(models.Model):
        title = models.CharField(max_length=32, verbose_name="出版社的名称")
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name_plural = "02-出版社表"
            db_table = verbose_name_plural
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32, verbose_name="作者的姓名")
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name_plural = "03-作者表"
            db_table = verbose_name_plural
    
    #DRFDemo中urls.py
    from django.contrib import admin
    from django.urls import path, include
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', include("SerDemo.urls")),
    ]
    #app01中urls.py
    from django.urls import path, include
    from .views import BookView, BookEditView
    urlpatterns = [
        path('list', BookView.as_view()),
        path('retrieve/<int:id>', BookEditView.as_view()),
    ]
    app01中models.py 和 app01中的urls.py
    #app01/views.py
    from django.views import View
    from django.http import HttpResponse, JsonResponse
    from django.core import serializers
    from .models import Book, Publisher
    
    class BookView(View):
        #第一版 用.values JsonResponse实现序列化
        def get(self, request):
            book_list = Book.objects.values("id", "title", "category", "pub_time", "publisher")
            book_list = list(book_list)
            # 如果需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成想要的
            ret = []
            for book in book_list:
                publisher_id = book["publisher"]
                publisher_obj = Publisher.objects.filter(id=publisher_id).first()
                book["publisher"] = {
                    "id": publisher_id,
                    "title": publisher_obj.title
                }
                ret.append(book)
            # ret = json.dumps(book_list, ensure_ascii=False)
            # return HttpResponse(ret) #时间
            return JsonResponse(ret, safe=False, json_dumps_params={"ensure_ascii": False})
    
        #第二版 用django serializers实现序列化
        # 能够得到要的效果,但是结构有点复杂,而且choices不能显示对应的文本
        def get(self, request):
            book_list = Book.objects.all()
            ret = serializers.serialize("json", book_list, ensure_ascii=False)
            return HttpResponse(ret)
    JsonResponse和Django序列化

    DRF的视图

    • 在Django REST Framework中内置的Request类扩展了Django中的Request类,实现了很多方便的功能--如请求数据解析和认证等。
    • 比如,区别于Django中的request从request.GET中获取URL参数,从request.POST中取某些情况下的POST数据。
    • 在APIView中封装的request,就实现了请求数据的解析:
    • 对于GET请求的参数通过request.query_params来获取。
    • 对于POST请求、PUT请求的数据通过request.data来获取。

    一、源码查找

    1. django中写CBV的时候继承的是View,rest_framework继承的是APIView
    2. 不管是View还是APIView最开始调用的都是as_view()方法。
    3. APIView继承了View, 并且执行了View中的as_view()方法,最后用csrf_exempt()方法包裹view把view返回了,用csrf_exempt()方法包裹后去掉了csrf的认证。
    4. 在View中的as_view方法返回了view函数,而view函数执行了self.dispatch()方法,但是这里是APIView调用的,所以先从APIView找dispatch()方法。
    5. APIView的dispatch()方法中给request重新赋值了
    6. 去initialize_request中看下把什么赋值给了request,并且赋值给了self.request, 也就是视图中用的request.xxx到底是什么
    7. 可以看到,这个方法返回的是Request这个类的实例对象,注意看下这个Request类中的第一个参数request,是走django的时候的原来的request
    8. 去Request这个类里看 这个Request类把原来的request赋值给了self._request, 也就是说以后_request是老的request,新的request是这个Request类
    9. request.query_params 存放的是get请求的参数 request.data 存放的是所有的数据,包括post请求的以及put,patch请求
    10. 相比原来的django的request,现在的request更加精简,清晰了
    11. 框架提供了一个路由传参的方法ViewSetMixin
    #1.徒手垒代码阶段
    class SchoolView(APIView):
        def get(self, request, *args, **kwargs):
            query_set = models.School.objects.all()
            ser_obj = app01_serializers.SchoolSerializer(query_set, many=True)
            return Response(ser_obj.data)
    
    
    class SchoolDetail(APIView):
        def get(self, request, pk, *args, **kwargs):
            obj = models.School.objects.filter(pk=pk).first()
            ser_obj = app01_serializers.SchoolSerializer(obj)
            return Response(ser_obj.data)
    
    #路由
    url(r'school/$', views.SchoolView.as_view()),
    url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()),
    
    #2.使用混合类阶段
    
    class SchoolView(GenericAPIView, mixins.ListModelMixin):
        queryset = models.School.objects.all()
        serializer_class = app01_serializers.SchoolSerializer
    
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
    
    class SchoolDetail(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin):
        queryset = models.School.objects.all()
        serializer_class = app01_serializers.SchoolSerializer
    
        def get(self, request, pk, *args, **kwargs):
            return self.retrieve(request, pk, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    #路由
    url(r'school/$', views.SchoolView.as_view()),
    url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()),
    
    
    # 3.使用通用类
    
    class SchoolView(ListCreateAPIView):
        queryset = models.School.objects.all()
        serializer_class = app01_serializers.SchoolSerializer
    
    
    class SchoolDetail(RetrieveUpdateDestroyAPIView):
        queryset = models.School.objects.all()
        serializer_class = app01_serializers.SchoolSerializer
    
    #路由
    url(r'school/$', views.SchoolView.as_view()),
    url(r'school/(?P<pk>\d+)/$', views.SchoolDetail.as_view()),
    
    #4.使用视图集
    
    class SchoolView(ModelViewSet):
        queryset = models.School.objects.all()
        serializer_class = app01_serializers.SchoolSerializer
    
    #路由:
    url(r'school/$', views.SchoolView.as_view({
        "get": "list",
        "post": "create",
    })),
    url(r'school/(?P<pk>\d+)/$', views.SchoolView.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    })),
    
    #高级路由
    
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    router.register(r'school', views.SchoolView)
    urlpatterns += router.urls
    视图和路由

    二、ModelViewSet

    from django.urls import path, include
    from .views import BookView, BookEditView, BookModelViewSet
    urlpatterns = [
        # path('list', BookView.as_view()),
        # path('retrieve/<int:id>', BookEditView.as_view()),
        path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
        path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
    ]
    app01中urls.py
    from .models import Book
    from .serializers import BookSerializer
    
    from rest_framework.viewsets import ModelViewSet
    class BookModelViewSet(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
    #现在的视图就只要写两行就可以了
    #注意:用框架封装的视图~url上的那个关键字参数要用pk系统默认的
    ##path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))  用pk
    
    # from rest_framework import views
    # from rest_framework import generics
    # from rest_framework import mixins
    # from rest_framework import viewsets
    app01中views.py
    from .models import Book
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .serializers import BookSerializer
    
    class GenericAPIView(APIView):
        query_set = None
        serializer_class = None
    
        def get_queryset(self):
            return self.query_set
    
        def get_serializer(self, *args, **kwargs):
            return self.serializer_class(*args, **kwargs)
    
    
    class ListModelMixin(object):
        def list(self, request):
            queryset = self.get_queryset()
            ret = self.get_serializer(queryset, many=True)
            return Response(ret.data)
    
    
    class CreateModelMixin(object):
        def create(self, request):
            serializer = self.get_serializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            else:
                return Response(serializer.errors)
    
    
    class RetrieveModelMixin(object):
        def retrieve(self, request, id):
            book_obj = self.get_queryset().filter(id=id).first()
            ret = self.get_serializer(book_obj)
            return Response(ret.data)
    
    
    class UpdateModelMixin(object):
        def update(self, request, id):
            book_obj = self.get_queryset().filter(id=id).first()
            serializer = self.get_serializer(book_obj, data=request.data, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            else:
                return Response(serializer.errors)
    
    
    class DestroyModelMixin(object):
        def destroy(self, request, id):
            book_obj = self.get_queryset().filter(id=id).first()
            book_obj.delete()
            return Response("")
    
    
    class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
        pass
    
    class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        pass
    
    # class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    class BookView(ListCreateAPIView):
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request):
            # book_obj = Book.objects.first()
            # ret = BookSerializer(book_obj)
            # book_list = Book.objects.all()
            # book_list = self.get_queryset()
            # ret = self.get_serializer(book_list, many=True)
            # return Response(ret.data)
            return self.list(request)
    
        def post(self, request):
            # print(request.data)
            # serializer = BookSerializer(data=request.data)
            # if serializer.is_valid():
            #     serializer.save()
            #     return Response(serializer.data)
            # else:
            #     return Response(serializer.errors)
            return self.create(request)
    
    
    # class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    class BookEditView(RetrieveUpdateDestroyAPIView):
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request, id):
            # book_obj = Book.objects.filter(id=id).first()
            # ret = BookSerializer(book_obj)
            # return Response(ret.data)
            return self.retrieve(request, id)
    
        def put(self, request, id):
            # book_obj = Book.objects.filter(id=id).first()
            # serializer = BookSerializer(book_obj, data=request.data, partial=True)
            # if serializer.is_valid():
            #     serializer.save()
            #     return Response(serializer.data)
            # else:
            #     return Response(serializer.errors)
            return self.update(request, id)
    
        def delete(self, request, id):
            # book_obj = Book.objects.filter(id=id).first()
            # book_obj.delete()
            # return Response("")
            return self.destroy(request, id)
    
    
    
    # class ViewSetMixin(object):
    #     def as_view(self):
    #         """
    #         按照我们参数指定的去匹配
    #         get-->list
    #         :return:
    #         """
    
    
    from rest_framework.viewsets import ViewSetMixin    #必须继承ViewSetMixin,路由的as_view方法才可以传参
    
    
    class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        pass
    
    
    #上面封装的所有框架都帮我们封装好了
    #from rest_framework.viewsets import ModelViewSet
    #注意:用框架封装的视图url上的那个关键字参数要用pk系统默认的
    
    class BookModelViewSet(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    自己封装ModelViewSet
    from rest_framework import views
    from rest_framework import generics
    from rest_framework import mixins
    from rest_framework import viewsets
    
    
    from bms import models
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from bms.modelserializers import BookModelSerializer, PublisherModelSerializer, AuthorModelSerializer
    
    
    class BookListView(APIView):
        def get(self, request):
            # 1. 查出所有的书籍信息
            queryset = models.Book.objects.all()  # [Book_obj1, Book_obj2, ...]
            # 2. 使用serizlizer序列化
            ser_obj = BookModelSerializer(queryset, many=True)  # [obj1, obj2, obj3, ...]
            return Response(ser_obj.data)  #
    
        def post(self, request):
            # 2. 对数据做有效性校验
            ser_obj = BookModelSerializer(data=request.data)
            if ser_obj.is_valid():
                ser_obj.save()  # 调用的是BookSerializer类中的create方法,需要自己去实现
                # 3. 拿到序列化的数据去数据库创建新记录
                return Response("ok")
            else:
                return Response(ser_obj.errors)
    
    
    class BookDetailView(APIView):
        """这是书籍详情相关的接口 支持:GET/PUT/DELETE"""
        def get(self, request, pk):
            """获取具体某本书的信息"""
            # 1. 根据pk去查询具体的那本书籍对象
            book_obj = models.Book.objects.filter(pk=pk).first()
            if book_obj:
                # 2. 将书籍对象 序列化成 json格式的数据
                ser_obj = BookModelSerializer(book_obj)
                # 3. 返回响应
                return Response(ser_obj.data)
            else:
                return Response("无效的书籍id")
    
        def put(self, request, pk):
            """修改具体某一本书"""
            # 1. 根据pk去查询具体的那本书籍对象
            book_obj = models.Book.objects.filter(pk=pk).first()
            if book_obj:
                # 2. 获取用户 发送过来的数据并且更新对象
                ser_obj = BookModelSerializer(instance=book_obj, data=request.data, partial=True)  # form组件中也有类似的实现
                if ser_obj.is_valid():
                    # 3. 保存并返回修改后的数据
                    ser_obj.save()
                    return Response(ser_obj.data)
                else:
                    return Response(ser_obj.errors)
            else:
                return Response("无效的书籍id")
    
        def delete(self, request, pk):
            """删除具体某一本书"""
            # 1. 根据pk去查询具体的那本书籍对象
            book_obj = models.Book.objects.filter(pk=pk)
            if book_obj:
                # 删除书籍对象
                book_obj.delete()
                return Response("删除成功")
            else:
                return Response("无效的书籍id")
    
    
    #####################进化
    class GenericAPIView(APIView):
        """把视图中可能用到的配置和方法封装起来"""
        queryset = None
        serializer_class = None
    
        def get_queryset(self, request, *args, **kwargs):
            ######### 让每次请求来的时候都现查一次数据
            return self.queryset.all()
    
        def get_object(self, request, pk, *args, **kwargs):
            return self.get_queryset(request, *args, **kwargs).filter(pk=pk).first()
    
    
    # python mixin(混合类):不能单独使用,和其它类搭配起来使用(利用了Python支持多继承)
    class ListModelMixin(object):
        def list(self, request, *args, **kwargs):  ##get
            queryset = self.get_queryset()
            ser_obj = self.serializer_class(queryset, many=True)
            return Response(ser_obj.data)
    
    
    class CreateModelMixin(object):
        def create(self, request, *args, **kwargs):  ##post
            ser_obj = self.serializer_class(data=request.data)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response("ok")
            else:
                return Response(ser_obj.errors)
    
    # 获取具体某一条记录
    class RetrieveModelMixin(object):
        def retrieve(self, request, pk, *args, **kwargs):  ##get
            obj = self.get_object(request, pk, *args, **kwargs)
            if obj:
                # 2. 将书籍对象 序列化成 json格式的数据
                ser_obj = self.serializer_class(obj)
                # 3. 返回响应
                return Response(ser_obj.data)
            else:
                return Response("无效的id")
    
    
    class UpdateModelMixin(object):
        def update(self, request, pk, *args, **kwargs):   ##put
            obj = self.get_object(request, pk, *args, **kwargs)
            if obj:
                ser_obj = self.serializer_class(instance=obj, data=request.data, partial=True)  # form组件中也有类似的实现
                if ser_obj.is_valid():
                    ser_obj.save()
                    return Response(ser_obj.data)
                else:
                    return Response(ser_obj.errors)
            else:
                return Response("无效的id")
    
    
    class DestroyModelMixin(object):
        def destroy(self, request, pk, *args, **kwargs):   ##delete
            obj = self.get_object(request, pk, *args, **kwargs)
            if obj:
                obj.delete()
                return Response("删除成功")
            else:
                return Response("无效的id")
    
    
    # 出版社
    class PublisherListView(GenericAPIView, ListModelMixin, CreateModelMixin):
        queryset = models.Publisher.objects.all()
        serializer_class = PublisherModelSerializer
    
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    
    class PublisherDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        queryset = models.Publisher.objects.all()
        serializer_class = PublisherModelSerializer
    
        def get(self, request, pk, *args, **kwargs):
            return self.retrieve(request, pk, *args, **kwargs)
    
        def delete(self, request, pk, *args, **kwargs):
            return self.destroy(request, pk, *args, **kwargs)
    
        def put(self, request, pk, *args, **kwargs):
            return self.update(request, pk, *args, **kwargs)
    
    
    #####################超进化##两个视图
    
    class CreateAPIView(CreateModelMixin,GenericAPIView):
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
    
    class RetrieveAPIView(RetrieveModelMixin,GenericAPIView):
        def get(self, request, pk, *args, **kwargs):
            return self.retrieve(request, pk, *args, **kwargs)
    
    class DestroyAPIView(DestroyModelMixin,GenericAPIView):
        def delete(self, request, pk, *args, **kwargs):
            return self.destroy(request, pk, *args, **kwargs)
    
    class UpdateAPIView(UpdateModelMixin,GenericAPIView):
        def put(self, request, pk, *args, **kwargs):
            return self.update(request, pk, *args, **kwargs)
    
    # 作者
    class AuthorListView(ListAPIView,CreateAPIView):
        queryset = models.Author.objects.all()
        serializer_class = AuthorModelSerializer
    
    
    class AuthorDetailView(RetrieveAPIView,DestroyAPIView,UpdateAPIView):
        queryset = models.Author.objects.all()
        serializer_class = AuthorModelSerializer
    
    
    ##########最终######一个视图搞定####必须继承ViewSetMixin,路由的as_view方法才可以传参
    
    ##配合url##actions
    urlpatterns = [
    
        # url(r'publishers/$', views.PublisherListView.as_view()),  # 出版社列表
        # url(r'publishers/(?P<pk>\d+)/$', views.PublisherDetailView.as_view()),  # 出版社详情
        #
        # url(r'authors/$', views.AuthorListView.as_view()),  # 作者列表
        # url(r'authors/(?P<pk>\d+)/$', views.AuthorDetailView.as_view()),  # 作者详情
    
        url(r'authors/$', views.AuthorViewSet.as_view(actions={'get': 'list', 'post': 'create'})),  # 作者列表
        url(r'authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view(
            actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})
            ),  # 作者详情
    
    ]
    
    # 或者
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    router.register('authors', views.AuthorViewSet)
    
    urlpatterns += router.urls
    
    ##########
    
    from rest_framework.viewsets import GenericViewSet
    class ViewSetMixin(object):
        def as_view(cls, actions=None, **initkwargs):
          # ...
          pass
    
    
    class GenericViewSet(ViewSetMixin, GenericAPIView):
        pass
    
    
    class AuthorViewSet(GenericViewSet,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,ListModelMixin):
        """
            list()
            create()
            retrieve()
            update()
            destroy()
    
        """
        queryset = models.Author.objects.all()
        serializer_class = AuthorModelSerializer
    
    
    
    
    
    #########################rest_framework###############################
    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        pass
    
    
    from rest_framework.viewsets import ModelViewSet
    class AuthorViewSet(ModelViewSet):
        """
            list()
            create()
            retrieve()
            update()
            destroy()
    
        """
        queryset = models.Author.objects.all()
        serializer_class = AuthorModelSerializer
    视图进化

    DRF的路由

    from django.urls import path, include
    from .views import BookView, BookEditView, BookModelViewSet
    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()
    router.register(r"book", BookModelViewSet)
    urlpatterns = [
        # path('list', BookView.as_view()),
        # path('retrieve/<int:id>', BookEditView.as_view()),
        #path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
        #path('retrieve/<int:pk>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
    ]
    urlpatterns += router.urls
    
    #通过框架可以把路由视图都变的非常简单
    #但是需要自定制的时候还是需要用APIView写,当不需要那么多路由的时候,也不要用这种路由注册.
    app01中urls.py

    DRF的版本

    随着项目的更新,版本就越来越多,不可能新的版本出了,以前旧的版本就不进行维护了,就需要对版本进行控制了

    一、源码查找

    1. APIView返回View中的view函数,然后调用dispatch方法,APIView的dispatch方法
    2. 执行self.initial方法之前是各种赋值,包括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)   #####
                  ...
      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.
                  # 版本控制
                  # self.determine_version 这个方法是找我们自己定义的版本控制类,没有的话返回(None,None)
                  version, scheme = self.determine_version(request, *args, **kwargs)
                  request.version, request.versioning_scheme = version, scheme
                  ###version版本信息赋值给了 request.version  版本控制方案赋值给了 request.versioning_scheme
                  ###其实这个版本控制方案~就是我们配置的版本控制的类,也就是说,APIView通过这个方法初始化自己提供的组件
                  ###接下来看看框架提供了哪些版本的控制方法在rest_framework.versioning里,from rest_framework import versioning
      
                  # Ensure that the incoming request is permitted
                  # 认证 权限 频率组件
                  self.perform_authentication(request)
                  self.check_permissions(request)
                  self.check_throttles(request)
      
      
      ...
      
          def determine_version(self, request, *args, **kwargs):
              """
              If versioning is being used, then determine any API version for the
              incoming request. Returns a two-tuple of (version, versioning_scheme)
              """
              if self.versioning_class is None:
                  return (None, None)
              #scheme是我们配置的版本控制类的实例化对象
              scheme = self.versioning_class()
              #返回值scheme.determine_version  MyVersion中必须定义determine_version这个方法,从上面可以看出此方法返回版本号version
              return (scheme.determine_version(request, *args, **kwargs), scheme)
      源码查找

    二、使用方法1(URL上携带版本信息的配置)

    第1步:settings.py

    REST_FRAMEWORK = {
        # 默认使用的版本控制类
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        # 允许的版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        # 版本使用的参数名称
        'VERSION_PARAM': 'version',
        # 默认使用的版本
        'DEFAULT_VERSION': 'v1',
    }
    settings.py

    第2步:app01中urls.py

    urlpatterns = [
        url(r"^(?P<version>[v1|v2]+)/test01", TestView.as_view()),
    ]
    app01.urls.py

    第3步:测试视图app01.views.py

    class TestView(APIView):
        def get(self, request, *args, **kwargs):
            print(request.versioning_scheme)
            ret = request.version
            if ret == "v1":
                return Response("版本v1的信息")
            elif ret == "v2":
                return Response("版本v2的信息")
            else:
                return Response("根本就匹配不到这个路由")
    app01.views.py

    三、使用方法2(URL过滤条件配置版本信息)

    第1步:settings.py

    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
        # "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
        "DEFAULT_VERSION": "v1",
        "ALLOWED_VERSIONS": ['v1', 'v2'],
        "VERSION_PARAM": "version"
    }
    settings.py

    第2步:app01中urls.py

    from django.urls import path, include
    from .views import DemoView
    
    urlpatterns = [
        path(r"", DemoView.as_view()),
    ]
    app01中urls.py

    第3步:utils中version.py

    from rest_framework import versioning
    class MyVersion(object):
        def determine_version(self, request, *args, **kwargs):
            # 返回值 给了request.version
            # 返回版本号
            # 版本号携带在过滤条件 xxxx?version=v1中,版本号在那就去那取值
            version = request.query_params.get("version", "v1")
            return version
    View Code

    第4步:测试视图app01.views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class DemoView(APIView):
        def get(self, request):
            print(request.version)
            print(request.versioning_scheme)
            # 得到版本号  根据版本号的不同返回不同的信息
            if request.version == "v1":
                return Response("v1版本的数据")
            elif request.version == "v2":
                return Response("v2版本的数据")
            return Response("不存在的版本")
    app01.views.py

    DRF的认证

    每次给服务器发请求,由于Http的无状态,导致每次都是新的请求,
    服务端需要对每次来的请求进行认证,看用户是否登录,以及登录用户是谁,
    服务器对每个请求进行认证的时候,不可能在每个视图函数中都写认证,
    一定是把认证逻辑抽离出来,以前我们可能会加装饰器或者中间件。

    一、源码查找

     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
    
    ...
        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
            )
    
    
        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.
            # 版本控制
            # self.determine_version 这个方法是找我们自己定义的版本控制类,没有的话返回(None,None)
            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)
    
    ...
        def perform_authentication(self, request):
            """
            Perform authentication on the incoming request.
    
            Note that if you override this and simply 'pass', then authentication
            will instead be performed lazily, the first time either
            `request.user` or `request.auth` is accessed.
            """
            request.user
    ...
    #去类Request中找user        
    
        @property
        def user(self):
            """
            Returns the user associated with the current request, as authenticated
            by the authentication classes provided to the request.
            """
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    #__enter__
                    self._authenticate()
                    #__exit__
            return self._user
    ...
    
        def _authenticate(self):
            """
            Attempt to authenticate the request using each authentication instance
            in turn.
            """
            #这里的authentications是最开始实例化Request类的时候传过来的
            #是调用get_authenticators这个方法,
            # 这个方法的返回值是 return [auth() for auth in self,authentication_classes]
            #authentication_classes如果我们配置了就用我们配置的,否则就从默认配置文件中读取配置类
            #返回的auth()是认证类实例化后的
            for authenticator in self.authenticators:   #查看authenticators
                try:
                    #也就是说这里的authenticator是认证类实例化后的
                    #authenticate方法是我们必须去实现的方法
                    #authenticate的参数self,我们是通过新的request.user进来的,所以这个self就是新的request
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    #request.user
                    #request.auth
                    self.user, self.auth = user_auth_tuple
                    return
    
            self._not_authenticated()
    
    ...
    class Request(object):
    
        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__)
            )
    
            self._request = request
            self.parsers = parsers or ()
            self.authenticators = authenticators or ()
            ## authenticators看传参了么
    ...
    
        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
            )
    
    ...
    
        def get_authenticators(self):
            """
            Instantiates and returns the list of authenticators that this view can use.
            """
            #self.authentication_classes去配置文件拿所有的认证类
            return [auth() for auth in self.authentication_classes]
    源码查找
    • APIView的dispatch方法里给request重新赋值了
    • APIView的dispatch方法里给执行了initial方法,初始化了版本认证,权限,频率组件,initial方法的参数request是重新赋值后的
    • 权限组件返回的是request.user,initial的request是重新赋值之后的,所以这里的request是重新赋值之后的,也就是Request类实例对象, 那这个user一定是一个静态方法.

    二、使用方法

    1、app01中models.py

    # 先在model中注册模型类
    # 并且进行数据迁移
    
    from django.db import models
    
    class User(models.Model):
    
        username = models.CharField(max_length=32)
        pwd = models.CharField(max_length=16)
        token = models.UUIDField()
    app01中models.py

    2、app01中urls.py

    from django.urls import path
    from .views import DemoView, LoginView, TestView
    
    
    urlpatterns = [
        path(r"login", LoginView.as_view()),
        path(r"test", TestView.as_view()),
    ]
    app01中urls.py

    3、app01中views.py

    import uuid
    from app01 import models
    from utils.auth import MyAuth
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    
    class LoginView(APIView):
        def post(self, request):
            username = request.data.get("username")
            pwd = request.data.get("pwd")
            if username and pwd:
                obj = models.User.objects.filter(username=username, pwd=pwd).first()
                if obj:
                    token = uuid.uuid4()
                    obj.token = token
                    obj.save()
                    return Response({'error_no': 0, 'token': token})
                else:
                    return Response("用户名密码错误")
            return Response("用户名密码不能为空")
    
    
    # 局部视图认证
    class TestView(APIView):
        authentication_classes = [MyAuth, ]
    
        def get(self, request):
            print(request.user)
            print(request.auth)
            user_id = request.user.id
            return Response("认证测试")
    app01中views.py

    4、utils中auth.py 写一个认证的类

    from rest_framework.exceptions import AuthenticationFailed
    from app01.models import User
    from rest_framework.authentication import BaseAuthentication
    
    
    class MyAuth(BaseAuthentication):
    
        def authenticate(self, request):
            # 做认证 看他是否登录
            # 拿到token,此处是从url过滤条件里拿到token
            # 去数据库看token是否合法
            # 合法的token能够获取用户信息
            token = request.query_params.get("token", "")
            if not token:
                raise AuthenticationFailed("没有携带token")
            user_obj = User.objects.filter(token=token).first()
            if not user_obj:
                raise AuthenticationFailed("token不合法")
            # return (None, None)  
            return (user_obj, token) #第1个返回值是request.user 第2个返回值是request.auth
    utils中auth.py

    5、全局配置认证

    REST_FRAMEWORK = {
        # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
        "DEFAULT_VERSION": "v1",
        "ALLOWED_VERSIONS": "v1, v2",
        "VERSION_PARAM": "ver",
        # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
    }
    settings.py

    DRF的权限

    对某件事情决策的范围和程度叫做权限

    一、源码查找

        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():
                #permission我们写的权限类的实例对象 MyPermission()
                if not permission.has_permission(request, self):
                    #permission_denied是抛出异常的
                    #也就是说我们的权限类中必须有has_permission这个方法,否则就抛出异常
                    self.permission_denied(
                         #message 定义异常信息
                        request, message=getattr(permission, 'message', None)
                    )
    源码查找
    • 权限类一定要有has_permission方法,否则就会抛出异常,这也是框架提供的钩子
    • rest_framework.permissions这个文件中存放了框架提供的所有权限的方法
    • BasePermission 这个是写权限类继承的一个基础权限类
    • Python代码是一行一行执行的,那么执行initial方法初始化这些组件的时候 也是有顺序的,版本在前面然后是认证,然后是权限最后是频率
    • 版本,认证,权限,频率这几个组件的源码是一个流程

    二、使用方法

    1、app01中models.py

    # 先在model中注册模型类
    # 并且进行数据迁移
    
    from django.db import models
    
    class User(models.Model):
    
        username = models.CharField(max_length=32)
        pwd = models.CharField(max_length=16)
        token = models.UUIDField()
        type = models.IntegerField(choices=((1, "vip"), (2, "vvip"), (3, "普通")), default=3)
    app01中models.py

    2、app01中urls.py

    from django.urls import path
    from .views import DemoView, LoginView, TestView
    
    
    urlpatterns = [
        path(r"login", LoginView.as_view()),
        path(r"test", TestView.as_view()),
    ]
    app01中urls.py

    3、app01中views.py

    import uuid
    from .models import User
    from utils.auth import MyAuth
    from utils.permission import MyPermission
    # Create your views here.
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    
    class DemoView(APIView):
        def get(self, request):
            return Response("认证demo~")
    
    
    class LoginView(APIView):
    
        def post(self, request):
            username = request.data.get("username")
            pwd = request.data.get("pwd")
            # 登录成功 生成token 会把token给你返回
            token = uuid.uuid4()
            User.objects.create(username=username, pwd=pwd, token=token)
            return Response("创建用户成功")
    
    
    class TestView(APIView):
        authentication_classes = [MyAuth,]
        permission_classes = [MyPermission, ]  #局部配置权限
    
        def get(self, request):
            print(request.user)
            print(request.auth)
            user_id = request.user.id
            return Response("认证测试")
    app01中views.py

    4、utils中permission.py 写一个权限类

    from rest_framework.permissions import BasePermission
    
    class MyPermission(BasePermission):
        message = "您没有权限"
    
        def has_permission(self, request, view):
            # request.user:当前经过认证的用户对象,
            # 如果没有认证 request.user 就是匿名用户
            if not request.user:
                # 认证没有通过
                return False
            # 判断用户是否有权限
            if request.user.type == 1:
                return True
            else:
                return False
    utils中permission.py

    5、全局配置权限

    REST_FRAMEWORK = {
        # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
        "DEFAULT_VERSION": "v1",
        "ALLOWED_VERSIONS": "v1, v2",
        "VERSION_PARAM": "ver",
        # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
    }
    REST_FRAMEWORK = {
        # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
        # 默认使用的版本控制类
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        # 允许的版本
        "ALLOWED_VERSIONS": "v1, v2",
        # 版本使用的参数名称
         "VERSION_PARAM": "ver",
        # 默认使用的版本
        'DEFAULT_VERSION': 'v1',
        # 配置全局认证
        # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
        # 配置全局权限
        "DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"]
    }
    settings.py

    DRF的频率

    开放平台的API接口调用需要限制其频率,以节约服务器资源和避免恶意的频繁调用。

    一、源码查找

        def check_throttles(self, request):
            """
            Check if request should be throttled.
            Raises an appropriate exception if the request is throttled.
            """
            #throttle 配置每个频率控制类的实例化对象   allow_request方法和wait方法
            for throttle in self.get_throttles():
                if not throttle.allow_request(request, self):
                    self.throttled(request, throttle.wait())
    
    ...
        def get_throttles(self):
            """
            Instantiates and returns the list of throttles that this view uses.
            """
            return [throttle() for throttle in self.throttle_classes]        
    源码查找

    二、频率组件原理

    DRF中的频率控制基本原理是基于访问次数和时间的,当然也可以通过自己定义的方法来实现。
    
    当请求进来,走到频率组件的时候,DRF内部会有一个字典来记录访问者的IP,
    
    以这个访问者的IP为key,value为一个列表,存放访问者每次访问的时间,
    
    {  IP1: [第三次访问时间,第二次访问时间,第一次访问时间],}
    
    把每次访问最新时间放入列表的最前面,记录这样一个数据结构后,通过什么方式限流呢~~
    
    如果我们设置的是10秒内只能访问5次,
    
      -- 1,判断访问者的IP是否在这个请求IP的字典里
    
      -- 2,保证这个列表里都是最近10秒内的访问的时间
    
          判断当前请求时间和列表里最早的(也就是最后的)请求时间的查
    
          如果差大于10秒,说明请求以及不是最近10秒内的,删除掉,
    
          继续判断倒数第二个,直到差值小于10秒
    
      -- 3,判断列表的长度(即访问次数),是否大于我们设置的5次,
    
          如果大于就限流,否则放行,并把时间放入列表的最前面。
    
    

    三、使用方法

    1、app01中views.py

    import uuid
    from .models import User
    from utils.auth import MyAuth
    from utils.permission import MyPermission
    from utils.throttle import MyThrottle
    # Create your views here.
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class LoginView(APIView):
    
        def post(self, request):
            username = request.data.get("username")
            pwd = request.data.get("pwd")
            # 登录成功 生成token 会把token给你返回
            token = uuid.uuid4()
            User.objects.create(username=username, pwd=pwd, token=token)
            return Response("创建用户成功")
    
    
    class TestView(APIView):
        authentication_classes = [MyAuth,]
        permission_classes = [MyPermission, ]
        throttle_classes = [MyThrottle, ]
    
        def get(self, request):
            print(request.user)
            print(request.auth)
            user_id = request.user.id
            return Response("认证测试")
    app01中views.py

    2、utils中throttle.py 写一个频率类

    from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
    import time
    
    VISIT_RECORD = {}
    
    #自定义的频率限制类
    
    # class MyThrottle(BaseThrottle):
    #
    #     def __init__(self):
    #         self.history = None
    #
    #     def allow_request(self, request, view):
    #         # 实现限流的逻辑
    #         # 以IP限流
    #         # 访问列表 {IP: [time1, time2, time3]}
    #         # 1, 获取请求的IP地址
    #         ip = request.META.get("REMOTE_ADDR")
    #         # 2,判断IP地址是否在访问列表
    #         now = time.time()
    #         if ip not in VISIT_RECORD:
    #             # --1, 不在 需要给访问列表添加key,value
    #             VISIT_RECORD[ip] = [now,]
    #             return True
    #             # --2 在 需要把这个IP的访问记录 把当前时间加入到列表
    #         history = VISIT_RECORD[ip]
    #         history.insert(0, now)
    #         # 3, 确保列表里最新访问时间以及最老的访问时间差 是1分钟
    #         while history and history[0] - history[-1] > 60:
    #             history.pop()
    #         self.history = history
    #         # 4,得到列表长度,判断是否是允许的次数
    #         if len(history) > 3:
    #             return False
    #         else:
    #             return True
    #
    #     def wait(self):
    #         # 返回需要再等多久才能访问
    #         time = 60 - (self.history[0] - self.history[-1])
    #         return time
    
    #使用自带的频率限制类
    class MyThrottle(SimpleRateThrottle):
        scope = "WD"
    
        def get_cache_key(self, request, view):
            # 如果以IP地址做限流返回IP地址
            key = self.get_ident(request)
            return key
    utils中throttle.py

    3、全局配置频率

    REST_FRAMEWORK = {
        # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
        "DEFAULT_VERSION": "v1",
        "ALLOWED_VERSIONS": "v1, v2",
        "VERSION_PARAM": "ver",
        # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
    }
    REST_FRAMEWORK = {
        # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
        # 默认使用的版本控制类
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        # 允许的版本
        "ALLOWED_VERSIONS": "v1, v2",
        # 版本使用的参数名称
         "VERSION_PARAM": "ver",
        # 默认使用的版本
        'DEFAULT_VERSION': 'v1',
        # 配置全局认证
        # "DEFAULT_AUTHENTICATION_CLASSES": ["utils.auth.MyAuth", ]  #全局配置
        # 配置全局权限
        "DEFAULT_PERMISSION_CLASSES": ["utils.permission.MyPermission"],
        # 配置自定义频率限制
        "DEFAULT_THROTTLE_CLASSES": ["Throttle.throttle.MyThrottle"],
        # 配置频率限制
        "DEFAULT_THROTTLE_RATES": {
            "WD": "3/m"   #速率配置每分钟不能超过3次访问,WD是scope定义的值,
        }
    }
    settings.py

    DRF的分页组件

    • DRF提供的三种分页: from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
    • 全局配置: REST_FRAMEWORK = { 'PAGE_SIZE': 2 }
    • 第1种 PageNumberPagination 看第n页,每页显示n条数据 http://127.0.0.1:8000/book?page=2&size=1
    • 第2种 LimitOffsetPagination 在第n个位置 向后查看n条数据 http://127.0.0.1:8000/book?offset=2&limit=1
    • 第3种 CursorPagination 加密游标的分页 把上一页和下一页的id记住 http://127.0.0.1:8000/book?page=2&size=1

    一、utils中pagination.py(自定义分页类)

    from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
    
    # class MyPagination(PageNumberPagination):
    #     # xxxx?page=1&size=2
    #     page_size = 1  # 每页显示多少条
    #     page_query_param = "page"   # URL中页码的参数
    #     page_size_query_param = "size"  # URL中每页显示条数的参数
    #     max_page_size = 3    # 最大页码数限制
    
    # class MyPagination(LimitOffsetPagination):
    #     # xxxx?offset=120&limit=20  #从第120条开始往后查20条数据
    #     default_limit = 1
    #     limit_query_param = "limit"
    #     offset_query_param = "offset"
    #     max_limit = 3
    
    class MyPagination(CursorPagination):
        #
        cursor_query_param = "cursor"
        page_size = 2
        ordering = "-id" #
    utils中pagination.py

    二、app01中views.py

    from django.shortcuts import render
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from SerDemo.models import Book
    from SerDemo.serializers import BookSerializer
    
    # Create your views here.
    from rest_framework import pagination
    from utils.pagination import MyPagination
    from rest_framework.generics import GenericAPIView
    from rest_framework.mixins import ListModelMixin
    
    
    # class BookView(APIView):
    #
    #     def get(self, request):
    #         queryset = Book.objects.all()
    #         # 1,实例化分页器对象
    #         page_obj = MyPagination()
    #         # 2,调用分页方法去分页queryset
    #         page_queryset = page_obj.paginate_queryset(queryset, request, view=self)
    #         # 3,把分页好的数据序列化返回
    #         # 4, 带着上一页下一页连接的响应
    #         ser_obj = BookSerializer(page_queryset, many=True)
    #         # 返回带超链接 需返回的时候用内置的响应方法
    #         return page_obj.get_paginated_response(ser_obj.data)
    
    
    class BookView(GenericAPIView, ListModelMixin):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        pagination_class = MyPagination
        # self.paginate_queryset(queryset)
    
        def get(self, request):
            return self.list(request)
    app01中views.py

    三、全局配置

    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
        'PAGE_SIZE': 20  #每页显示的数量
    }
    settings.py

    DRF的解析器

    • 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己想要的数据类型的过程。本质就是对请求体中的数据进行解析。
    • 在了解解析器之前要先知道Accept以及ContentType请求头。
    • Accept是告诉对方能解析什么样的数据,通常也可以表示想要什么样的数据。
    • ContentType是告诉对方我给你的是什么样的数据类型。
    • 解析器工作原理的本质 就是拿到请求的ContentType来判断前端给后端数据类型是什么,然后后端去拿相应的解析器去解析数据。
    • from django.core.handlers.wsgi import WSGIRequest

    一、Django的解析器

    • 请求进来请求体中的数据在request.body中,那也就证明,解析器会把解析好的数据放入request.body
    • 在视图中可以打印request的类型,能够知道request是WSGIRequest这个类。
    • application/x-www-form-urlencoded不是不能上传文件,是只能上传文本格式的文件
    • multipart/form-data是将文件以二进制的形式上传,这样可以实现多种类型的文件上传 一个解析到request.POST, request.FILES中。
    • 也就是说之前能在request中能到的各种数据是因为用了不同格式的数据解析器
    • Django只能解析cont_type=multipart/form-data 和cont_type=application/x-www-form-urlencoded的数据,不能解析json

    二、DRF的解析器

    • 在request.data拿数据的时候解析器会被调用

    四、DRF的解析器使用方法

    1、app01中views.py

    from django.shortcuts import render
    from django.views import View
    from django.http import HttpResponse
    from django.core.handlers.wsgi import WSGIRequest
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.negotiation import DefaultContentNegotiation
    from rest_framework import parsers
    
    # Create your views here.
    
    
    class DjangoView(View):
        def get(self, request):
            print(type(request))
            # Request
            # request.GET
            # request.POST
            # json request.body
            return HttpResponse("django解析器测试~~")
    
    
    class DRFView(APIView):
        #parser_classes = [parsers.JSONParser, ]  #一般不配置
    
        def get(self, request):
            # request 重新封装的request  Request
            # request.data
            #
            return Response("DRF解析器的测试~~")
    app01中views.py

    DRF的渲染器

    渲染器就是友好的展示数据,我们在浏览器中展示的DRF测试的那个页面就是通过浏览器的渲染器来做到的,当然我们可以展示Json数据类型

    DEFAULTS = {
        # Base API policies
        'DEFAULT_RENDERER_CLASSES': (
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ),
    DRF的渲染器
  • 相关阅读:
    RPM包校验和提取
    RPM包查询
    Find命令简介
    无法启动配置好的虚拟机
    文档发布至博客操作说明
    VMware Virtual Machine安装报错解决1
    python create home dircetory
    Centos7/Active Directory authentication using nss-pam-ldapd
    java try后面括号的作用
    vps上搭建jupyter notebook远程服务
  • 原文地址:https://www.cnblogs.com/bubu99/p/10487215.html
Copyright © 2020-2023  润新知