drf-day4

发布时间 2023-08-31 16:42:54作者: Py玩家

昨日回顾

1、cbv源码分析

路由匹配成功》》》配置再路由上的第二个参数:执行了as_view的内存地址(request)》》》执行父类闭包函数view(request)》》》执行self.dispatch》》》视图类中的类没有》》》在父类找到执行》》》通过反射找到与请求方式方通的的方法》》》执行此方法,传入request.

2、apiview执行流程

路由匹配成功》》》》BookView.as_view()》》》APIView的as_view配置在路由上的第二个参:执行函数内存地址(request)》》》执行View的view(request)》》》但是它去除了csrf认证》》》dispatch》》》APIView的dispatch---》
  1. 包装了新的request---》视图类的方法中的request是新的
  2. 执行三大认证
  3 .处理了全局异常

3、新的request

1. request.data 方法被包装成了数据属性
2. 地址栏中的参数 请求参数 request.query_params 本质就是request._request.GET
3 .用其他的跟之前一模一样
  request.method》》》request._request.method

4、序列化组件

drf提供的一个可以做序列化和反序列化的类

作用:序列化、反序列化、数据校验

6、如何做序列化

1.创建一个serializer.py文件,写一个类继承serialzers.Serializer

2.写序列化字段,字段参数

3.在视图中:ser=BookSerializer(instance=序列化的对象,many=True)

4.ser.data

7、反序列化校验

ser=BookSerializer(data=request.data)
if ser.is_valid:三层校验:字段自己,局部钩子,全局钩子
    ser.errors:返回错误信息

8、反序列化保存

ser.save():需要配合序列化类中的重写create类来使用

函数与方法

from types import MethodType, FunctionType

# 一切皆对象,函数也是个对象, 由某个类产生     FunctionType
def add():
    pass

print(isinstance(add, FunctionType))  # 判断一个对象是不是这个类的对象
print(isinstance(add, MethodType))  # 判断一个对象是不是这个类的对象

class Foo:
    def run(self):
        pass

    @classmethod
    def xx(cls):
        pass


    @staticmethod
    def zz():
        pass

# 对象调用 run ,run就是方法 会自动传值
f=Foo()
print(isinstance(f.run,FunctionType))
prnt(isinstance(f.run,MethodType))

# 类来调用run,run就是函数,有几个值就要传几个值
print(isinstance(Foo.run,FunctionType))
print(isinstance(Foo.run,MethodType))
# 类调用类的绑定方法---》就是方法
print(isinstance(Foo.xx,FunctionType))
print(isinstance(Foo.xx,MethodType))

# 对象调用类的绑定方法---》 也是方法
print(isinstance(f.xx,FunctionType))
print(isinstance(f.xx,MethodType))
# 对象来调用
print(isinstance(f.zz,FunctionType))  # True
print(isinstance(f.zz,MethodType))    #False
# 类来调用
print(isinstance(Foo.zz,FunctionType))  # True
print(isinstance(Foo.zz,MethodType))    #False

5个接口(apiview+response+序列化类)

1.接口代码

2.总结

1.序列化类》》》做序列化
多条一定要写,many=Ture
2.序列化校验
ser.is_valid()  就会走校验
3.反序列化保存
新增:
ser = BookSerializer(data=request.data)
ser.save()触发序列化类中的create方法,判断instance是否有值
create中,自己写保存到哪个表中:有数据》》》保存到某个表中
修改:
ser = BookSerializer(instance=待修改对象,data=request.data)
ser.save()》》》触发 序列化类中的 update》》》为什么?内部做了判断:根据是否有instance
update中,有待修改对象,有数据---》修改完保存即可--》两种方式

反序列化之更新

视图类

class BookDetailView(APIView):
    def put(self, request, pk, *args, **kwargs):
        # 要用查出来的对象,使用传入的数据,做修改
        book=Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book,data=request.data) # 使用前端传入的数据,修改book
        if ser.is_valid():
            ser.save()  # 调用了序列化类的save:内部会触发序列化类中 update方法的执行     不是book.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

序列化类

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.CharField()

    # 重写create  前端传入,校验过后的数据validated_data
    def create(self, validated_data):
        book = Book.objects.create(**validated_data)  # 前端传入的key值必须跟数据库字段一致的
        return book

    # 如果是修改,需要重写update方法

    def update(self, instance, validated_data):
        # instance 就是pk  2
        # Book.objects.filter(pk=instance).update(**validated_data)
        # instance 待修改的对象   咱们在 view中的那个book
        # validated_data 校验过后的数据   本质还是  request.data 经过了数据校验
        # 方式一:
        # instance.name=validated_data.get('name')
        # instance.price=validated_data.get('price')
        # instance.publish=validated_data.get('publish')
        # instance.save()

        # 方式二: 反射是通过字符串动态的获取或设置属性或方法
        # get=getattr(self,'get')
        # get()

        # setattr(instance,'name','西游记') ---》 instance.name='西游记'
        for k in validated_data:  # {"name":"西游记","price":99,"publish":南京出版社}
            setattr(instance, k, validated_data.get(k))
            # instance.publish=validated_data.get('publish')
        instance.save()
        return instance

高级用法source

用法1:在序列化类中拿到表中字段

xxx = serializers.CharField(source='name')

前端:"xxx": "三毛流浪记"
用法2:跨表查询
(外键字段) publish = serializers.CharField(source='publish.name') # 自动对应成出版社的名字 可以通过 .  跨表查询

用法3:表模型中写方法,拿到方法的返回值

表模型:

@property
def get_name(self):
  return self.name+'sb'

序列化类:yyy = serializers.CharField(source='get_name')

前端:"yyy": "三毛流浪记sb"

高级用法定制字段

定制返回字段的格式,publish也是一个对象

{"name": "信息","price": 12,"publish": {name:xx,addr:xx}}

方案一:使用SerializerMethodField 定制

在序列化类中使用SerializerMethodField
    publish_detail = serializers.SerializerMethodField()
    def get_publish_detail(self, obj): # 返回什么,序列化后publish就是什么
        # obj 就是序列化到的book对象
        return {'name':obj.publish.name,'addr':obj.publish.addr}

方案er: 在表模型中定制

1 表模型中写方法,包装成数据属性

@property
def publish_dict(self):
       return {'name': self.publish.name}

2 序列化类中

publish_dict=serializers.DictField()

多表关联序列化和反序列化

以后一个序列化类,想即做序列化,又做反序列化,会出现问题:字段不匹配,尤其是多表关联的字段

有的字段 即做序列化,又做反序列化

name = serializers.CharField()
price = serializers.IntegerField()

有的字段:只做序列化(read_only表示 只做序列化)

publish_dict = serializers.DictField(read_only=True)  # 只做序列化
    author_list = serializers.ListField(read_only=True)  # 只做序列化

有的字段只做反序列化(write_only=True)》》》是什么类型,取决于前端传入的格式什么样

publish_id = serializers.IntegerField(write_only=True)  # 反序列化
authors = serializers.ListField(write_only=True)  # 反序列化

保存方法需要自己重写

    def create(self, validated_data):  # {"name":"三国演义","price":999,"publish":1,"authors":[1,2]}
        authors=validated_data.pop('authors')
        book = Book.objects.create(**validated_data)
        # 增加中间表的记录:图书和作者的关系
        book.authors.add(*authors)  # 向中间表中存入:这个图书关联的做作者

        return book

笨办法:

序列化用一个序列化类
反序列化换另一个序列化类

反序列化校验总结

什么情况用反序列化校验

做反序列化的时候,才用》》》校验前端传入的数据

三层:

字段自己:字段类属性---》可控制的比较小
局部钩子:单个字段校验
全局钩子:多个字段同时校验

拓展:

新增传这种数据过来,新增图书,新增一个作者,新增一个出版社
{"name":"新西游记111","price":199,"publish_id":{name:西安出版社,addr:西安},"authors":[{"name": "lqz","sex": "男","age": 19}]}