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("必须重写该方法,否则抛错")
原文地址:https://www.cnblogs.com/feiguoguobokeyuan/p/14012515.html