• django-rest-framework


    参考博客

    [置顶]Python系列教程 | Justin-刘清政的博客

    app01/ser.py代码

    from django.db import models
    
    # Create your models here.
    class Book(models.Model):
        id=models.AutoField(primary_key=True)
        name=models.CharField(max_length=32)
        price=models.DecimalField(max_digits=5,decimal_places=2)
        author=models.CharField(max_length=32)
        publish=models.CharField(max_length=32)
    
    # from rest_framework.serializers import Serializer  # 就是一个类
    from rest_framework import serializers
    from rest_framework.exceptions import ValidationError
    # 需要继承 Serializer
    from app01.models import Book
    
    def check_author(data):
        if data.startswith('sb'):
            raise ValidationError('作者名字不能以sb开头')
        else:
            return data
    
    
    class BookSerializer(serializers.Serializer):
        id=serializers.CharField(read_only=True)
        name=serializers.CharField(max_length=16,min_length=4)
        # price=serializers.DecimalField()
        price=serializers.CharField(write_only=True,required=True)
        author=serializers.CharField(validators=[check_author])  # validators=[] 列表中写函数内存地址
        publish=serializers.CharField()
    
        def validate_price(self, data):   # validate_字段名  接收一个参数
            #如果价格小于10,就校验不通过
            # print(type(data))
            # print(data)
            if float(data)>10:
                return data
            else:
                #校验失败,抛异常
                raise ValidationError('价格太低')
        def validate(self, validate_data):   # 全局钩子
            print(validate_data)
            author=validate_data.get('author')
            publish=validate_data.get('publish')
            if author == publish:
                raise ValidationError('作者名字跟出版社一样')
            else:
                return validate_data
        def update(self, instance, validated_data):
            #instance是book这个对象
            #validated_data是校验后的数据
            instance.name=validated_data.get('name')
            instance.price=validated_data.get('price')
            instance.author=validated_data.get('author')
            instance.publish=validated_data.get('publish')
            instance.save()  #book.save()   django 的orm提供的
            return instance
        def create(self, validated_data):
            instance=Book.objects.create(**validated_data)
            return instance
            # Book.objects.create(name=validated_data.get('name'))
    
    
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=Book  # 对应上models.py中的模型
            fields='__all__'
            # fields=('name','price','id','author') # 只序列化指定的字段
            # exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
            # read_only_fields=('price',)
            # write_only_fields=('id',) #弃用了,使用extra_kwargs
            extra_kwargs = {  # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
                'price': {'write_only': True},
            }
    

    app01/views.py

    from django.shortcuts import render
    
    # Create your views here.
    from django.http import JsonResponse
    
    from rest_framework.views import APIView
    from app01.models import Book
    from app01.ser import BookSerializer
    from app01.ser import BookModelSerializer
    from rest_framework.response import Response  #drf 提供的响应对象
    
    # 导入自己写的response类
    from app01.utils import MyResponse
    class BookView(APIView):
        def get(self,request,pk):
            book=Book.objects.filter(id=pk).first()
            #用一个类,毫无疑问,一定要实例化
            #要序列化谁,就把谁传过来
            book_ser=BookSerializer(book)  # 调用类的__init__
            # book_ser.data   序列化对象.data就是序列化后的字典
            return Response(book_ser.data)
            # return JsonResponse(book_ser.data)
    
        def put(self,request,pk):
            response_msg={'status':100,'msg':'成功'}
            # 找到这个对象
            book = Book.objects.filter(id=pk).first()
            # 得到一个序列化类的对象
            # boo_ser=BookSerializer(book,request.data)
            boo_ser=BookSerializer(instance=book,data=request.data)
    
            # 要数据验证(回想form表单的验证)
            if boo_ser.is_valid():  # 返回True表示验证通过
                boo_ser.save()  # 报错
                response_msg['data']=boo_ser.data
            else:
                response_msg['status']=101
                response_msg['msg']='数据校验失败'
                response_msg['data']=boo_ser.errors
    
            return Response(response_msg)
    
        def delete(self,request,pk):
            response=MyResponse()
            ret=Book.objects.filter(pk=pk).delete()
            return Response(response.get_dict)
    
    class BooksView(APIView):
        # def get(self,request):
        #     response_msg = {'status': 100, 'msg': '成功'}
        #     books=Book.objects.all()
        #     book_ser=BookSerializer(books,many=True)  #序列化多条,如果序列化一条,不需要写
        #     response_msg['data']=book_ser.data
        #     return Response(response_msg)
    
        def get(self,request):
            response=MyResponse()
            books=Book.objects.all()
            book_ser=BookSerializer(books,many=True)  #序列化多条,如果序列化一条,不需要写
            response.data=book_ser.data
            return Response(response.get_dict)
    
        # 新增
        def post(self,request):
            response_msg = {'status': 100, 'msg': '成功'}
            #修改才有instance,新增没有instance,只有data
            book_ser = BookSerializer(data=request.data)
            # book_ser = BookSerializer(request.data)  # 这个按位置传request.data会给instance,就报错了
            # 校验字段
            if book_ser.is_valid():
                book_ser.save()
                response_msg['data']=book_ser.data
            else:
                response_msg['status']=102
                response_msg['msg']='数据校验失败'
                response_msg['data']=book_ser.errors
            return Response(response_msg)
    
    
    class BooksView2(APIView):
        def get(self,request):
            response=MyResponse()
            books=Book.objects.all()
            book=Book.objects.all().first()
            book_ser=BookModelSerializer(books,many=True)
            book_one_ser=BookModelSerializer(book)
            print(type(book_ser))
            print(type(book_one_ser))
            response.data=book_ser.data
            return Response(response.get_dict)
    
    
    

    APIView源码分析


    drf的Request类


    drf序列化

    序列化与反序列化介绍



    需要在installed_app中注册一下'rest_framework',不然报错,如果使用Jsonresponse返回,则可以不用注册
    
    序列化对象.data直接拿到json数据
    
    可以将rest_framework的response替换成jsonresponse,前者只是给返回的数据做了一个界面美化。
    

    序列化字段的类型

    使用charfield就返回字符串格式的json

    • 数据库定义的字段类型和序列化类写的字段可以不一样,都用charfield没问题
    • 路由
    • 序列化器
    • 需要查看哪些字段就写哪些字段
    • 字段选择
    • 将从数据库取出的数据传给序列化器,再使用response转成json返回(使用drf带的response需要先把rf注册到app,否则报错模板不存在)

    序列化字段选项

    • 使用接口,put,post数据时做数据校验

    • put方法修改数据

    • 之前的修改数据方式

    • 接口规范控制:抽象类abc、抛异常

    • 重写update,更新数据

    • 集成的父类中有这个父类内部调用这个方法,优先使用重写的这个方法

    序列化组件修改数据

    自定义校验:局部钩子、全局钩子校验:指定字段校验(类似form)

    数据保存之前,可以使用钩子校验规则
    validate_字段名     #局部钩子
    validate # 全局钩子
    

    字段访问控制

    • readonly只可以查看,不可以写的字段
    • writeonly只可以写,不可以查
    class BookSerializer(serializers.Serializer):
        id=serializers.CharField(read_only=True)
        name=serializers.CharField(max_length=16,min_length=4)
        # price=serializers.DecimalField()
        price=serializers.CharField(write_only=True,required=True)
        author=serializers.CharField(validators=[check_author])  # validators=[] 列表中写函数内存地址
        publish=serializers.CharField()
    
    • get
    • 序列化多条加many
    from app01.models import Book
    from app01.ser import BookSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response 
    class BooksView(APIView):
        def get(self,request):
            response=MyResponse()
            books=Book.objects.all()
            book_ser=BookSerializer(books,many=True)  #序列化多条,如果序列化一条,不需要写
            response.data=book_ser.data
            return Response(response.get_dict)
    
    • 新增post
    • 需要修改序列化类的create方法
    ser.py
    # from rest_framework.serializers import Serializer  # 就是一个类
    from rest_framework import serializers
    from rest_framework.exceptions import ValidationError
    # 需要继承 Serializer
    from app01.models import Book
    class BookSerializer(serializers.Serializer):
        id=serializers.CharField(read_only=True)
        name=serializers.CharField(max_length=16,min_length=4)
        # price=serializers.DecimalField()
        price=serializers.CharField(write_only=True,required=True)
        author=serializers.CharField(validators=[check_author])  # validators=[] 列表中写函数内存地址
        publish=serializers.CharField()
    
        def validate_price(self, data):   # validate_字段名  接收一个参数
            #如果价格小于10,就校验不通过
            #如果价格小于10,就校验不通过
            # print(type(data))
            # print(data)
            if float(data)>10:
                return data
            else:
                #校验失败,抛异常
                raise ValidationError('价格太低')
        def validate(self, validate_data):   # 全局钩子
            print(validate_data)
            author=validate_data.get('author')
            publish=validate_data.get('publish')
            if author == publish:
                raise ValidationError('作者名字跟出版社一样')
            else:
                return validate_data
        def update(self, instance, validated_data):
            #instance是book这个对象
            #validated_data是校验后的数据
            instance.name=validated_data.get('name')
            instance.price=validated_data.get('price')
            instance.author=validated_data.get('author')
            instance.publish=validated_data.get('publish')
            instance.save()  #book.save()   django 的orm提供的
            return instance
        def create(self, validated_data):
            instance=Book.objects.create(**validated_data)
            return instance
            # Book.objects.create(name=validated_data.get('name'))
    
    
    views.py
    from app01.models import Book
    from app01.ser import BookSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response 
        # 新增
        def post(self,request):
            response_msg = {'status': 100, 'msg': '成功'}
            #修改才有instance,新增没有instance,只有data
            book_ser = BookSerializer(data=request.data)
            # book_ser = BookSerializer(request.data)  # 这个按位置传request.data会给instance,就报错了
            # 校验字段
            if book_ser.is_valid():
                book_ser.save()
                response_msg['data']=book_ser.data
            else:
                response_msg['status']=102
                response_msg['msg']='数据校验失败'
                response_msg['data']=book_ser.errors
            return Response(response_msg)
    
    • 修改put
    • 需要传book实例
    • 需要重写update方法
    from app01.models import Book
    from app01.ser import BookSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response 
    class BookView(APIView):
        def put(self,request,pk):
            response_msg={'status':100,'msg':'成功'}
            # 找到这个对象
            book = Book.objects.filter(id=pk).first()
            # 得到一个序列化类的对象
            # boo_ser=BookSerializer(book,request.data)
            boo_ser=BookSerializer(instance=book,data=request.data)
    
            # 要数据验证(回想form表单的验证)
            if boo_ser.is_valid():  # 返回True表示验证通过
                boo_ser.save()  # 报错
                response_msg['data']=boo_ser.data
            else:
                response_msg['status']=101
                response_msg['msg']='数据校验失败'
                response_msg['data']=boo_ser.errors
    
            return Response(response_msg)
    
    • delete删除
    • 直接操作book对象,不需要重写方法
    from app01.models import Book
    from app01.ser import BookSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response 
        def delete(self,request,pk):
            response=MyResponse()
            ret=Book.objects.filter(pk=pk).delete()
            return Response(response.get_dict)
    
    • 类封装返回信息
    app1/uitls.py
    class MyResponse():
        def __init__(self):
            self.status=100
            self.msg='成功'
        @property
        def get_dict(self):
            return self.__dict__
    
    
    if __name__ == '__main__':
        res=MyResponse()
        res.status=101
        res.msg='查询失败'
        # res.data={'name':'lqz'}
        print(res.get_dict)
    

    • 类封装返回信息的使用

    模型类的序列化器

    • app/views.py
    from rest_framework.views import APIView
    from app01.models import Book
    from app01.ser import BookModelSerializer
    from rest_framework.response import Response  #drf 提供的响应对象
    # 导入自己写的response类
    from app01.utils import MyResponse
    class BooksView2(APIView):
        def get(self,request):
            response=MyResponse()
            books=Book.objects.all()
            book=Book.objects.all().first()
            book_ser=BookModelSerializer(books,many=True)
            book_one_ser=BookModelSerializer(book)
            print(type(book_ser))
            print(type(book_one_ser))
            response.data=book_ser.data
            return Response(response.get_dict)
    
    • app/ser.py
    • 对应上models.py中的模型,不需重写update和create
    from rest_framework import serializers
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=Book  # 对应上models.py中的模型
            fields='__all__'
            # fields=('name','price','id','author') # 只序列化指定的字段
            # exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
            # read_only_fields=('price',)
            # write_only_fields=('id',) #弃用了,使用extra_kwargs
            extra_kwargs = {  # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
                'price': {'write_only': True},
            }
    

    注册app

    路由分发

    Serializer高级用法

    source隐藏数据库字段,使用字段别名

    • 尽量隐藏数据库字段,保证与序列化字段名不一样

    小结

    1 Serializer类,需要序列化什么,必须写一个类继承,想序列化什么字段,
    就在里面写字段,source的作用(很多字段类)
    
    2 序列化queryset(列表)对象和真正的对象,many=True的作用,instance=要序列化的对象,
    
    3 反序列化 instance=要序列化的对象,data=request.data
    
    4 字段验证,序列化类中,给字段加属性,局部和全局钩子函数,字段属性的validators=[check_author]
    
    5 当在视图中调用 序列化对象.is_valid()   
      boo_ser.is_valid(raise_exception=True) 只要验证不通过,直接抛异常
    
    6 修改保存---》调用序列化对象.save(),重写Serializer类的update方法
    	    def update(self, instance, validated_data):
                #instance是book这个对象
                #validated_data是校验后的数据
                instance.name=validated_data.get('name')
                instance.price=validated_data.get('price')
                instance.author=validated_data.get('author')
                instance.publish=validated_data.get('publish')
                instance.save()  #book.save()   django 的orm提供的
                return instance
    
    7 序列化得到字典,序列化对象.data  
    
    8 自己定义了一个Response对象
    	class MyResponse():
            def __init__(self):
                self.status=100
                self.msg='成功'
            @property
            def get_dict(self):
                return self.__dict__
            
    9 反序列化的新增 序列化类(data=request.data),如果只传了data,当调用  
    序列化对象.save(),会触发序列化类的create方法执行,
    当传了instance和data时,调用 序列化对象.save(),会触发序列化类的update方法执行
    
    10 重写create方法(可以很复杂)
    	    def create(self, validated_data):
                instance=Book.objects.create(**validated_data)
                return instance
    
    
    11 ModelSerializer 跟Model做了一个对应
    	class BookModelSerializer(serializers.ModelSerializer):
            def validate_price(self, data):
                pass
            publish=serializers.CharField(source='publish.name')
            class Meta:
                model=Book  # 对应上models.py中的模型
                fields='__all__'
                # fields=('name','price','id','author','publish') # 只序列化指定的字段
                # exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
                # read_only_fields=('price',)
                # write_only_fields=('id',) #弃用了,使用extra_kwargs
                extra_kwargs = {  # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
                    'price': {'write_only': True,max_length:16,min_length:4},
                    
                }
                
    12 如果在ModelSerializer中写一个局部钩子或者全局钩子,如何写?
    	-跟之前一模一样
    
    13 many=True 能够序列化多条的原因---》__new__是在__init__之前执行的,造出一个空对象
    
    14 接口:统一子类的行为
    

    drf-request和drf-response

    • __getattr__方法
    定义了__getattr__(),当访问object不存在的属性时会调用该方法
    
    不定义访问不存在的属性时会报 AttributeError
    
    eg:
    
    class Cat(object):
      def __init__(self):
        self.name = "jn"
    
      def __getattr__(self, item):
        return "tm"
    
    
    cat = Cat()
    print(cat.name)
    print(getattr(cat, 'name'))
    print("*" * 20)
    print(cat.age)
    print(getattr(cat, 'age'))
    

    getattr()函数

    >>> getattr(a, 'bar')        # 获取属性 bar 值
    1
    

    • assert断言该传入的request对象是django传入的request对象,如果不是则返回

    请求对象.data:前端以三种编码方式传入的数据,都可以取出来

    text/html
    application/json
    

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

    drf-response

    #from rest_framework.response import Response
     def __init__(self, data=None, status=None,
                     template_name=None, headers=None,
                     exception=False, content_type=None):
            
    #data:你要返回的数据,字典
    #status:返回的状态码,默认是200,
    	-from rest_framework import status在这个路径下,它把所有使用到的状态码都定义成了常量
    #template_name 渲染的模板名字(自定制模板),不需要了解
    #headers:响应头,可以往响应头放东西,就是一个字典
    #content_type:响应的编码格式,application/json和text/html;
    
    # 浏览器响应成浏览器json格式,postman响应成json格式,通过配置实现的(默认配置)
    
    #不管是postman还是浏览器,都返回json格式数据
    # drf有默认的配置文件---》先从项目的setting中找,找不到,采用默认的
    # drf的配置信息,先从自己类中找--》项目的setting中找---》默认的找
    	-局部使用:对某个视图类有效
            -在视图类中写如下
            from rest_framework.renderers import JSONRenderer
            renderer_classes=[JSONRenderer,]
        -全局使用:全局的视图类,所有请求,都有效
        	-在setting.py中加入如下
            REST_FRAMEWORK = {
                'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
                    'rest_framework.renderers.JSONRenderer',  # json渲染器
                    'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器浏览器访问时,会返回浏览器格式
                )
            }
    

    • status响应状态码

    视图

    • 可以找到api.html页面修改当前显示

    全局和局部配置响应

    • 访问视图的查找顺序

    视图

    # 两个视图基类
    APIView
    GenericAPIView
    

    基于GenericAPIView写5个接口

    # views.py
    class Book2View(GenericAPIView):
        #queryset要传queryset对象,查询了所有的图书
        # serializer_class使用哪个序列化类来序列化这堆数据
        queryset=Book.objects
        # queryset=Book.objects.all()
        serializer_class = BookSerializer
        def get(self,request):
            book_list=self.get_queryset()
            book_ser=self.get_serializer(book_list,many=True)
    
            return Response(book_ser.data)
        def post(self,request):
            book_ser = self.get_serializer(data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'status':101,'msg':'校验失败'})
    
    
    class Book2DetailView(GenericAPIView):
        queryset = Book.objects
        serializer_class = BookSerializer
        def get(self, request,pk):
            book = self.get_object()
            book_ser = self.get_serializer(book)
            return Response(book_ser.data)
    
        def put(self, request,pk):
            book = self.get_object()
            book_ser = self.get_serializer(instance=book,data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'status': 101, 'msg': '校验失败'})
    
        def delete(self,request,pk):
            ret=self.get_object().delete()
            return Response({'status': 100, 'msg': '删除成功'})
        
     #url.py
        # 使用GenericAPIView重写的
        path('books2/', views.Book2View.as_view()),
        re_path('books2/(?P<pk>d+)', views.Book2DetailView.as_view()),
    
    • GenericAPIView源码


      -GenricApiView的好处

    基于GenericAPIView和5个视图扩展类写的接口

    from rest_framework.mixins import  ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
    # views.py
    class Book3View(GenericAPIView,ListModelMixin,CreateModelMixin):
    
        queryset=Book.objects
        serializer_class = BookSerializer
        def get(self,request):
            return self.list(request)
    
        def post(self,request):
            return self.create(request)
    
    class Book3DetailView(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin):
        queryset = Book.objects
        serializer_class = BookSerializer
        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)
    # urls.py
        # 使用GenericAPIView+5 个视图扩展类  重写的
        path('books3/', views.Book3View.as_view()),
        re_path('books3/(?P<pk>d+)', views.Book3DetailView.as_view()),
    

    GenericApiView的9个视图子类

    • 进一步省略代码
    • 使用继承就可以使用
    • get
    • post
    • 最终代码

    retrieve获取一条,listApiView获取多条
    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
    

    使用ModelViewSet编写5个接口

    • 源码分析
    • viewsets重写了路由的as_view方法


    • 最终代码:路由增加一个参数,来区分get一条或者多条


      代码
    # views.py
    from rest_framework.viewsets import ModelViewSet
    class Book5View(ModelViewSet):  #5个接口都有,但是路由有点问题
        queryset = Book.objects
        serializer_class = BookSerializer
        
    # urls.py
    # 使用ModelViewSet编写5个接口
        path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #当路径匹配,又是get请求,会执行Book5View的list方法
        re_path('books5/(?P<pk>d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
    
    
    

    源码分析ViewSetMixin

    Python全栈开发29期_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili

    # 重写了as_view
    # 核心代码(所以路由中只要配置了对应关系,比如{'get':'list'}),当get请求来,就会执行list方法
    for method, action in actions.items():
        #method:get
        # action:list
        handler = getattr(self, action)
        #执行完上一句,handler就变成了list的内存地址
        setattr(self, method, handler)
        #执行完上一句  对象.get=list
        #for循环执行完毕 对象.get:对着list   对象.post:对着create
    

    继承viewsetmixin视图类

    • 自定义get方法使用某个函数
    # views.py
    from rest_framework.viewsets import ViewSetMixin
    class Book6View(ViewSetMixin,APIView): #一定要放在APIVIew前,也可以继承genricapiview
        def get_all_book(self,request):
            print("xxxx")
            book_list = Book.objects.all()
            book_ser = BookSerializer(book_list, many=True)
            return Response(book_ser.data)
        
    # urls.py
        #继承ViewSetMixin的视图类,路由可以改写成这样
        path('books6/', views.Book6View.as_view(actions={'get': 'get_all_book'})),
    
    

    https://tva1.sinaimg.cn/large/007S8ZIlgy1ggipvctuxcj31wc0goq89.jpg

    微信:jinmuqq222
  • 相关阅读:
    整理了8个Python中既冷门又实用的技巧
    python中68个内置函数的总结
    Python中常见的8种数据结构的实现方法(建议收藏)
    python基础教程:dir()和__dict__属性的区别
    Python 优雅获取本机 IP 方法
    Python类中的self到底是干啥的
    python中反转列表的三种方式
    Flask学习笔记(2)-login_page
    利用Flask + python3.6+MYSQL编写一个简单的评论模块。
    最近写了个自动填写调查的问卷的简单爬虫
  • 原文地址:https://www.cnblogs.com/jinmuqq222/p/14380673.html
Copyright © 2020-2023  润新知