zoukankan      html  css  js  c++  java
  • drf3

    昨日回顾

    1 restful规范
    	-只是一个规范,规范了前后端交互的接口(api接口)格式
        -10条
        	-https部署
            -请求地址中又接口标识
            	-https://api.baidu.com
                -https://www.baidu.com/api/
            -多版本共存
            	-https://www.baidu.com/api/v1/
            -请求资源名词表示,可以复数
            	-一切皆资源
                -https://www.baidu.com/api/v1/books/
            -请求方式表示操作资源的方式
            	-get:获取资源
                -post:新增资源
                -put:修改
                -patch:修改
                -delete:删除资源
           	-响应中带状态码
            	-{code:100}
            -响应中带错误信息
            	-{code:100,msg:'错误'}
            -响应中带链接地址
            -响应数据遵循以下格式
            	-多条数据  []
                -单条      {}
                -新增      新增的数据返回
                -修改      修改的数据返回
                -删除      空文档
            -请求中带过滤条件
     2 序列化器
    	-把对象----》序列化器---》字典-----》Response--->json格式字符串给前端
        -把前端传过来的数据---Request-》字典---》序列化器----》对象---》保存
        -数据校验
        -如何使用
        	-写一个序列化类 继承Serializer
            -在类中写字段
            	-字段类型:CharField...
                -字段属性参数:required...
            -在视图类中使用
            	-实例化得到对象
                	-序列化(对象--》字典)ser=XXSerialier(instance=对象,many=True)----》ser.data
                    -反序列化(新增)(字典---》对象)ser=XXSerialier(data=字典)
                    
       -反序列化的校验(三种方式)
    		-字段自己的校验
        	-validators=[函数内存地址,]
            -局部和全局钩子
            	-validate_字段名(self,data)
                -validate(self,attrs)
      -wirte_only和read_only	
    

    今日内容

    1 修改,删除接口

    views.py

        def put(self, request, id):
            # 通过id取到对象
            res = {'code': 100, 'msg': ''}
            try:
                book = models.Book.objects.get(id=id)
                ser = BookSerializer(instance=book, data=request.data)
                ser.is_valid(raise_exception=True)  # 如果写上了raise_exception=True那么就不用写if,校验通过继续往下执行,校验不通过会主动抛异常
                ser.save()
                res['msg'] = '修改成功'
                res['result'] = ser.data  # 比较规范的写法,修改成功后,返回的数据有状态码,信息,以及修改后的数据
    
            except Exception as e:
                res['code'] = 101
                res['msg'] = str(e)  # 一般用户看的话错误信息应该直接是未知异常错误,而不是哪行代码错误的信息,这个只是给自己内部排查看的。
    
            return Response(res)
        def delete(self,request,id):
            response = {'code': 100, 'msg': '删除成功'}
            res = models.Book.objects.filter(id=id).delete() # 返回的结果是一个元组,第一个参数是影响的行数,第二个参数是字典,里面的key是表模型,value是影响了多少行,一般我们需要知道影响的行数来进行判断,if res[0]>0,那么代表修改数据成功,否则失败
            return Response(response)
    

    serializer.py

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)
        title = serializers.CharField(max_length=32,min_length=2)
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        publish = serializers.CharField(max_length=32)
    
        def create(self, validated_data):
            res=models.Book.objects.create(**validated_data)
            print(res)
            return res
    # 要修改数据必须重写update方法
        def update(self, instance, validated_data):  # 不管里面有的数据改没改,都应该把数据给相对应
            instance.title=validated_data.get('title')
            instance.price=validated_data.get('price')
            instance.publish=validated_data.get('publish')
            instance.save()   # 此处是对象调用的save不是drf中的save,也就是说是book.save(),即此方法中所有的instance在此处全不可以换成对应的对象book
            return instance  
    

    2 高级用法之source(看源码)

    用法一 # 修改返回到前端的字段名,在序列化器中对应的字段处加上以下字段常数,加上什么名字,返回前端的就叫什么名字
    	# source=title    字段名就不能再叫title
    	name = serializers.CharField(max_length=32,min_length=2,source='title')
    2 # 如果在序列化器中写下了表中没有的字段xxx,而且source对应了方法的内存地址test,那么就会执行表模型中的对应内存地址(test)的方法,并且把返回值赋值给xxx
    	在序列器中 xxx=serializers.CharField(source='test')
        在表模型models中 例 def test(self):
            				……
            				return self.title+'aaa'
            				
    3 source支持跨表操作例建了另一张表publish,里面有字段name和addr,此处通过表名.字段名拿到了另一张表中的字段,在返回的数据中显示多了一条是以字典显示key为对应的addr,value为publish.adddr拿到另一张表的数据的类,要想显示名字,则应该重写该表的__str__方法(最后以json数据显示);# 或者在已有的字段中通过source='publish.name',将另一张表中字段的数据给该序列化中已有的字段,也能拿到另一张表中的数据
    	addr=serializers.CharField(source='publish.addr')
    
    # python是强类型语言,不支持字符串和数字类型直接相加,必须强转之后才行,类似的go语言也是强类型语言,而js是弱类型语言,会默认的将某一方自动转成另一个类型来进行运算,所以js是支持字符串和数字类型直接相加。
    # 脏数据,一般是因为公司在设计表的时候,不会建立外键约束,因此有的数据因为前面的代码没有考虑周全,导致了脏数据进入了数据表中,要想将这些脏数据删除,其中的办法有例如通过拿出关联的另一张表中的id,看它在不在(in)脏数据的哪个关联字段中。
    

    image-20201105161957907

    3 模型类序列化器

    1 # 原来用的Serilizer跟表模型没有直接联系, 模型类序列化器ModelSerilizer跟表模型有对应关系
    2 使用
    	class BookModelSerializer(serializers.ModelSerializer):
            class Meta:
                model=models.Book    # 表示跟哪个表模型建立关系该处必须是model
                fields=[字段,字段] # 表示序列化Book中的字段和反序列化的字段,该处必须是fields,如果某些字段在表中没有,那么必须要在上面添加字段,例如表中没有publish,但fields中要写该字段,那么就需要像下面一样重写某些字段
                fields='__all__' # 对应表中所有字段都序列化,反序列化
                exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用),或者写成exclude=(字段,字段)元组的形式也可以
                read_only_fields=['price','publish']  # 序列化显示的字段
    			write_only_fields=['title']           # 反序列化需要传入的字段,可能有的版本不能用
                extra_kwargs ={'title':{'max_length':32,'write_only':True}}  # 给字段添加额外的参数,可以用于反序列化的校验
                depth=1  # 了解,跨表1次查询,最多建议写3,表示拿出跨表的所有字段
                
            # 重写某些字段,通过这种方法,能拿到其它关联表的字段,那么上面fields里就能拿到重写字段对应的值
            publish = serializers.CharField(max_length=32,source='publish.name') 
            
            # 局部钩子,全局钩子,跟原来完全一样
    3 用模型类序列化器新增,修改不用重写create和update方法了,因为这些方法在ModelSerializer源码内部已经重写了create和update,但是如果像保存到多个不同的表,而有的表就需要重写create方法
    

    4 高级用法之SerializerMethodField

    # 把出版社所有的信息取出
    class BookSerializer(serializers.Serializer):  # 此处用的是Serializer
        id = serializers.IntegerField(required=False)
        name = serializers.CharField(max_length=32,min_length=2,source='title')
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        publish = serializers.SerializerMethodField() # 写了SerializerMethodField后面就必须写def get_字段
        def get_publish(self,obj):  # 上面是啥字段,get后面跟的就是啥字段,obj是前面查询所传过来的对象,此处是Book的对象
            dic={'name':obj.publish.name,'addr':obj.publish.addr}
            return dic  # 返回是什么,前面publish接收的就是返回的结果
    
    class BookModelSerializer(serializers.ModelSerializer):  # 此处用的是ModelSerializer
        class Meta:
            model = models.Book
            fields = '__all__'
        publish = serializers.SerializerMethodField()  # 写了SerializerMethodField后面就必须写def get_字段
        def get_publish(self,obj):
            dic={'name':obj.publish.name,'addr':obj.publish.addr}
            return dic
    
    ## 第三中方案,使用序列化类的嵌套(这种方法用的比较多)
    class PublishSerializer(serializers.ModelSerializer):  # 首先直接序列化另一个表取对应的字段信息
        class Meta:
            model = models.Publish
            # fields = '__all__'
            fields = ['name','addr']
    
    class BookModelSerializer(serializers.ModelSerializer):
        publish = PublishSerializer()  # 调用另一个表中的字段信息
        class Meta:
            model = models.Book
            fields = '__all__'  # 自己表中的字段信息(另一个表中的字段优先使用自己的)
    

    5 drf的请求与相应

    # Request
    	-data :前端以post请求提交的数据都在它中
        -FILES :前端提交的文件
        -query_params:就是原来的request.GET
        -重写了 __getattr__
        	-使用新的request.method其实取得就是原生request.method(通过反射实现)
            
     # Response
    	-from rest_framework.response import Response
        -data:响应的字典
        -status:http响应的状态码
        	-drf提供了所有的状态码,以及它的意思
            from rest_framework.status import HTTP_201_CREATED
        -template_name:模板名字(一般不动),了解
        -headers:放响应头,字典
        -content_type:响应的编码方式,了解
        
     # 自己封装一个Response对象
          class CommonResponse:
            def __init__(self):
                self.code=100
                self.msg=''
            @property               # 因为调用的时候不想加(),所以加了属性装饰器
            def get_dic(self):
                return self.__dict__  # 去对象中找到所有变量并将其转换为字典
    # 自己封装一个response,继承drf的Response
    
    # 通过配置,选择默认模板的显示形式(浏览器方式,json方式)
    	-配置文件方式(全局)
        -如果没有配置,默认有浏览器和json(因为drf有默认配置文件,自己没有配,会使用drf默认),默认文件的地址在from rest_framework.settings import DEFAULTS
        -自己配置,在django的setting空白地方写下,还可以写其他的响应
                REST_FRAMEWORK = {   
                    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
                        'rest_framework.renderers.JSONRenderer',  # json渲染器
                        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器API渲染器
                    )
                    }
        -在视图类中配置(局部)只控制某类的渲染
        导入from rest_framework.renderers import JSONRenderer
            -class BookDetail(APIView):
        		renderer_classes=[JSONRenderer,] # 在需要的类中写下所需要的渲染器,那么该类显示输出的就是类中所配置的,其他的类中的不变
    # 改模板可以进drf中的templates的模板文件中修改,修改了之后显示就会变成自己修改的样式 
    

    7 many=True源码分析,局部全局钩子源码解析

    1 many=True  生成的是listserializer,many=False生成的对象是单个的bookserializer
    	-__init__----->一路找到了BaseSerializer---》__new__决定了生成的对象是谁(在源码中通过pop出many,如果是False,调用了父类的__new__方法,创建了父类的对象,如果是True,调用了cls.many_init方法,在many_init中生成了列表,把类的对象一个一个的放入列表中,然后返回了list_serializer_class)
        
    2 局部钩子和全局钩子入口是is_valid()---》BaseSerializer--》is_valid---》self._validated_data = self.run_validation(self.initial_data)
    	-Serializer这个类的:self.run_validation
    def run_validation(self, data=empty):
            value = self.to_internal_value(data)  # 局部字段自己的校验和局部钩子校验
            try:
                self.run_validators(value)
                value = self.validate(value)  # 全局钩子的校验
            except (ValidationError, DjangoValidationError) as exc:  # 捕获了两个异常
                raise ValidationError(detail=as_serializer_error(exc))
            return value
    

    拓展

    1 接口幂等性,是什么,如何弄?
    2 接口:统一子类的行为
    	抛异常限制
        abc模块限制
        人为限制(鸭子类型)
    
    '''
    1、如果子类在继承后一定要实现的方法,可以在父类中指定metaclass为abc模块的ABCMeta类,并在指定的方法上标准abc模块的@abcstractmethod来达到目的。
    2、一旦定义了这样的父类(有抽象的方法),父类就不能实例化了,否则会抛出TypeError异常。如果没有抽象类的方法,那么可以实例化。
    3、继承的子类如果没有实现@abcstractmethod标注的方法,在实例化使也会抛出TypeError异常,如果没有。
    继承有抽象方法的接口类但没有重写抽象方法=》报错,继承有抽象方法的接口类必须重写抽象方法,继承有抽象方法的非接口类呢==》不报错
    '''
    from abc import ABCMeta,abstractmethod
    class Tester(metaclass=ABCMeta): # 此是基类,可以实例化
        @abstractmethod
        def test(self):
            pass
     
    class FunctionTester(Tester):   # 正常
        def test(self):
            print("功能测试")
            
    class PerformanceTester(Tester):  # 错误
        def notest(self):
            print("因为没有定义test方法,故实例化会报错")
            
    # 源码中是通过以下的形式来要求必须继承父类的某一类方法,子类不重写该方法就主动抛错(重写的方法名必须一样)
    class Test:
    	def test(self):
    		raise NotImplementedError("必须重写该方法,否则抛错")
    
  • 相关阅读:
    UML(Unified Modeling Language)统一建模语言
    20、ASP.NET MVC入门到精通——WebAPI
    16、ASP.NET MVC入门到精通——MVC过滤器
    .net开发过程中Bin目录下面几种文件格式的解释
    13、ASP.NET MVC入门到精通——MVC请求管道
    15、ASP.NET MVC入门到精通——MVC-路由
    14、ASP.NET MVC入门到精通——Ajax
    10、ASP.NET MVC入门到精通——Model(模型)和验证
    12、ASP.NET MVC入门到精通——HtmlHelper
    8、ASP.NET MVC入门到精通——View(视图)
  • 原文地址:https://www.cnblogs.com/feiguoguobokeyuan/p/14012515.html
Copyright © 2011-2022 走看看