序列化和反序列化进一步升级方法

【一】 创建一个用户,授予所有库所有表的权限:允许本地连,允许远程连用本机的ip地址

1.进入数据库mysql -P3306 -uroot -proot -p
2.创建用户CREATE USER 'llh' @'%' IDENTIFIED BY 'llh011223';
3.授予权限 
对于远程连接:
GRANT ALL PRIVILEGES ON *.* TO 'llh'@'%' WITH GRANT OPTION;
对于本地连接(可选)
GRANT ALL PRIVILEGES ON *.* TO 'username'@'localhost' WITH GRANT OPTION;
4.刷新权限FLUSH PRIVILEGES;
5.用创建的用户进入数据库mysql -P3306 -ullh -pllh -p
​
启动mysql -P3306 -uroot -p

【二】反序列化之校验

2.1反序列化校验

1.回忆昨天的写序列化类
  -创建序列化类继承serializer
  -在视图类
    -取对象
    -放到创建的序列化类中进行序列化
    -返回Response(serializer.data)
2.校验--》有三层
    -第一层:字段自己的校验:写在字段类的属性上
        eg:age = serializers.IntegerField(required=True,maxvalue=100,validators=[函数内存地址])必填而且最大值不超过100,validators=[函数内存地址]是指校验完前面的就校验这个校验器里的
     
    -第二层:局部钩子校验:
        在序列化类中写validate_字段名
        -写法:
            def validate_name(self, name):
        if 'llh' in name:
            raise ValidationError('名字中不能带llh')
        else:
            # 校验通过,返回校验的字段名
            return name
        
        会报错"""
                    {
                "code": 201,
                "msg": {
                    "name": [
                        "名字中不能带llh"
                    ]
                }
            }
        """
        
    -第三层:全局钩子--》用来校验多个字段的,不是单个validate
       写法:
            def validate(self, attrs):
                print(attrs)#-->一个字典{'name': '阿华', 'age': 2, 'gender': 0}
                if attrs.get("name")==str(attrs.get("age")):
                    raise ValidationError('名字与年龄不能相同')
                    else:
                        return attrs
      报错:
                {
            "code": 201,
            "msg": {
                "non_field_errors": [
                    "名字与年龄不能相同"
                ]
            }
        }
        
        
        
3.view.py虽然在序列化类里已经有钩子函数主动抛出异常,但是在控制台是没有显示的
    def post(self,request):
        print(request)
        print(type(request.data))
        serializer=StudentSerializer(data=request.data)
        # 第一种校验写法
        # if serializer.is_valid():
        #     serializer.save()
        #     return Response(serializer.data)
        # 第二种校验写法
        serializer.is_valid(raise_exception=True)#这里校验不通过就不会去下面的保存了Bad Request: /api/v3/students/
        serializer.save()
        return Response(serializer.data)

【三】普通定制返回格式

3.1使用一:定制表中的某个字段

在序列化类中写
"""注意:字段名和source不能写一样的要不然会报错AssertionError: ... it is the same as the field name."""
意思是你要让该数据字段名你自己定制不要显示原来的
结果:
    [
        {
            "id": 2,
            "user": "阿坚",
            "age": 22,
            "gender": 1
        },
        {
            "id": 3,
            "user": "阿x",
            "age": 22,
            "gender": 0
        }]
 写法:
以上结果是get查询的
   #- 1.1view.py
        def get(self,request):
​
            obj=Student.objects.all()
            if obj:
                serializer=StudentSerializer(instance=obj,many=True)
                return Response(serializer.data)
            return Response({"code": 201, "msg": "查询失败"})
        
   #-1.2serializer.py
class StudentSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    user = serializers.CharField(source='name',required=False)
    age = serializers.IntegerField()
    gender = serializers.IntegerField(default=0)
    
   #-1.3models.py
from django.db import models
​
# Create your models here.
class Student(models.Model):
    name=models.CharField(max_length=30)
    age=models.IntegerField()
    gender=models.IntegerField(choices=((1,'男'),(2,'女'),(0,'未知')))
    
    """遇到的问题:发起post请求新增数据时user不是name值居然空的---》猜测因为post反序列化进去,要user为字段名会映射到name上,序列化时name映射到user"""---》缓存问题
​

3.2使用二:定制模型表中某个方法

在模型表中写
"""意思是在模型表中定义一个包装数据的方法,将某字段处理后返回,然后在序列类中映射这个方法名,得到的就是处理后的数据,名字也是也是可以成自定义的"""
结果:
[
    {
        "id": 2,
        "user": "阿坚_new",
        "age": 22,
        "gender": 1
    },
    {
        "id": 3,
        "user": "阿x_new",
        "age": 22,
        "gender": 0
    }]
写法:
#- 1.1view.py
与上同
​
 #-1.2serializer.py
class StudentSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    user = serializers.CharField(source='new_name',required=False)
    age = serializers.IntegerField()
    gender = serializers.IntegerField(default=0)
    
   #-1.3models.py 
class Student(models.Model):
    name=models.CharField(max_length=30)
    age=models.IntegerField()
    gender=models.IntegerField(choices=((1,'男'),(2,'女'),(0,'未知')))
#对字段数据加工处理
    def new_name(self):
        return self.name+'_new'
    
    """post与上问题一样"""---》缓存问题

3.3使用三:跨表查询

就是将某字段查找时进行跨表
#1.结果:
[
    {
        "user": "小红书",
        "price": 22,
        "publish": "东京出版社"
    },
    {
        "user": "小白书",
        "price": 33,
        "publish": "春风出版社"
    }
]
#2.写法:
和上面不同的只有序列化类不同
class BookSerialzier(serializers.Serializer):
    user = serializers.CharField(source='name')
    price = serializers.IntegerField()
    publish=serializers.CharField(source='publish.name')

【四】序列化之一对多关系定制返回格式

4.1定制返回格式方式一source(不太用)

就是渲染到那个对象就拿哪个对象的出版社--》把模型实例转成json格式
publish=serializers.CharField(source='publish.name')

4.2方式二:在表模型中写方法

意思就是在模型表中先对数据已经进行了处理到时候调用这个方法就能拿到我们要的数据--->在序列化类中的字段名要与表模型中的方法名一致
"""但是怎样拿的publish的信息是字符串,到时候想拿他的name之类的不好拿
"""
-1.结果:
    [
    {
        "user": "小红书",
        "price": 22,
        "publish_detail": "{'name': '东京出版社', 'addr': '东京'}"
    },
    {
        "user": "小白书",
        "price": 33,
        "publish_detail": "{'name': '春风出版社', 'addr': '上海'}"
    }
]
    
-2.写法:
view.py
与之前同
serializer.py-->变成拿我们处理的方法
class BookSerialzier(serializers.Serializer):
    user = serializers.CharField(source='name')
    price = serializers.IntegerField()
    publish_detail=serializers.CharField()
    
models.py
class Book3(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了--》不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')
    def publish_detail(self):
        return {"name":self.publish.name,"addr":self.publish.addr}

4.3方式三:在序列化类中写方法

在序列化中写方法get_字段名(self,obj)-->这个obj就是当前序列化到的模型类对象---》拿到的是一个对象
-1.结果:[
        {
            "user": "小红书",
            "price": 22,
            "publish": {
                "name": "东京出版社",
                "addr": "东京"
            }
        },
        {
            "user": "小白书",
            "price": 33,
            "publish": {
                "name": "春风出版社",
                "addr": "上海"
            }
        }
    ]
-2.写法:
    2.1view视图类相同
    2.2serializer.py-->变成也类似与修改序列化样子的方法
    class BookSerialzier(serializers.Serializer):
    user = serializers.CharField(source='name')
    price = serializers.IntegerField()
    # publish_detail=serializers.CharField()
    publish=serializers.SerializerMethodField()
    # 这个obj就是当前序列化到的对象
    def get_publish(self,obj):
        return {'name':obj.publish.name,'addr':obj.publish.addr}
    2.3模型类一致
    

4.4子序列化方法

1.就是在这之前先写了publish的序列化方法
"""前提是有表关联关系"""
2.
就序列化类不同结果与上也同
class PublishSerialzier(serializers.Serializer):
    name=serializers.CharField()
    addr=serializers.CharField()
    city = serializers.CharField()
class BookSerialzier(serializers.Serializer):
    user = serializers.CharField(source='name')
    price = serializers.IntegerField()
    publish=PublishSerialzier()
#若序列化和反序列化都需要与外键字段同名,但是只能有一个同名,要不然这个就变成了可读可写,

eg:我们在write_only的时候已经用了publish这个,但是我在read_only的时候,要用子序列化,那么又要用到这个publish外键字段怎么办
1.serialzier.py
class BookModelSerialzier(serializers.ModelSerializer):
    class Meta:
        model = Book3
        fields = ['id','name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']  # 新增的publish 也要注册
        extra_kwargs = {'name': {'max_length': 8}, 'price': {'max_value': 100},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }

        def validate_name(self, name):
            if "sb" in name:
                raise ValidationError("名字中不能带有sb")

    publish_detail = PublishSerialzier(read_only=True)
    author_list = serializers.ListField(read_only=True)
    
2.models.py
class Book3(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了--》不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')
    #就是这里
    def publish_detail(self):
        return self.publish
    # def publish_detail(self):
    #     return {"name":self.publish.name,"addr":self.publish.addr}
    def author_list(self):
        l=[]
        obj=self.authors.all()
        for i in obj:
            l.append({'name':i.name,'age':i.age})
        return l

 

【五】多表关联反序列化

1.多表关联序列化
	-source
	-表模型中写方法
	-序列化中写方法
	-子序列化
2.多表关联反序列化
	以新增图书为例
需求就是序列化就干序列化的事,反序列化就只干反序列化--》在序列化类中改写
1.序列化是把表模型的数据变成我们可读的数据格式--》查询
"""从数据库中查出所有数据"""

2.反序列化就是把我们可读的数据变成表模型的格式存入数据库--》新增
"""进行序列化我们给的出版社肯定是个id,然后我们再根据id进行绑定等操作"""

1.serializer.py

#在这里要注意的是反序列化对内存储的时候,请求体的数据的字段名key值要与我们在序列化类中写的字段名一致
如果没写write_only=True,read_only=True那么就是即可序列化又可以反序列化
class BookSerialzier(serializers.Serializer):
    user = serializers.CharField(source='name')
    price = serializers.IntegerField()
#     反序列化--》拿的事publish_id--》然后写入数据库
    publish_id=serializers.CharField(write_only=True)
    authors=serializers.ListField(write_only=True)
#     序列化---》就是把数据库的信息拿出来响应出去给别人看
#     用于处理字典(dictionary)和列表(list)类型的数据,我们给外面看的分别是一个字典和列表
    publish_detail=serializers.DictField(read_only=True)
    author_list=serializers.ListField(read_only=True)
    def create(self, validated_data):
        # validated_data={"name":"三国演义","price":19,"publish":1,"authors":[1,2]}
        authors = validated_data.pop('authors')  # {"name":"三国演义","price":19,"publish":1}
        print(validated_data)
        book = Book.objects.create(**validated_data)
        book.authors.add(*authors)
        return book
    
    
#发送的数据是
{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2]}
分析:一本书只有一个出版社,是一对多的关系,所以在图书表中显示的字段名为publish_id,那么他就不用处理,可以直接存进去,由于图书表中没有author字段,由于多对多,在第三张表上,也是用id号来填入的所以只要写操作第三张表的语句就行
# book.authors.clear() # 清空
# book.authors.add(*authors)
# book.authors.remove(*authors)  # 删除关系
book.authors.add(*authors)

2.modles.py

#在这里要要先对序列化给前端看的数据进行处理
class Book3(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了--》不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')
    def publish_detail(self):
        return {"name":self.publish.name,"addr":self.publish.addr}
    def author_list(self):
        l=[]
        obj=self.authors.all()
        for i in obj:
            l.append({'name':i.name,'age':i.age})
        return l

3.结果

[
    {
        "user": "小红书",
        "price": 22,
        "publish_detail": {
            "name": "东京出版社",
            "addr": "东京"
        },
        "author_list": [
            {
                "name": "llh",
                "age": 22
            },
            {
                "name": "lzj",
                "age": 22
            }
        ]
    },
    {
        "user": "小白书",
        "price": 33,
        "publish_detail": {
            "name": "春风出版社",
            "addr": "上海"
        },
        "author_list": [
            {
                "name": "lzj",
                "age": 22
            }
        ]
    }
]

【六】反序列化的常见问题

6.1问题一:

在反序列化中发送的请求体数据我对于publish,我写他的字典,我不写id
前端传的数据:{"name":"三国演义","price":19,"publish":{"name":"上海出版社","addr":"上海"},"authors":[{},{}]}

解决办法:就是在create的时候对数据进行处理
validated_data 就是传入的


序列化类的写法:
class BookSerialzier(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()
       #     反序列化--》拿的事publish_id--》然后写入数据库
    publish = serializers.DictField(write_only=True)
    authors = serializers.ListField(write_only=True)
     #     序列化---》就是把数据库的信息拿出来响应出去给别人看
    #     用于处理字典(dictionary)和列表(list)类型的数据,我们给外面看的分别是一个字典和列表
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)
     def create(self, validated_data):
        # validated_data={"name":"三国演义","price":19,"publish":1,"authors":[1,2]}
        authors = validated_data.pop('authors')  # {"name":"三国演义","price":19,"publish":1}
        print(validated_data)
        publish = validated_data.pop('publish')
        publish = Publish.objects.filter(name=publish["name"]).first().pk
        validated_data["publish_id"] = publish
        print(validated_data)
        book = Book3.objects.create(**validated_data)
        l=[]
        print(authors)
        for author in authors:
            people=Author.objects.filter(name=author["name"]).first().id
            print(people)
            l.append(people)
        print(l)
        book.authors.add(*l)
        return book
    
    #发送的数据是:
    {"name":"三国演义","price":19,"publish":{"name":"春风出版社","addr":"上海","city":"上海"},"authors":[{"name":"llh","age":22},{"name":"lzj","age":22}]}

6.2问题二

# 针对于 Author和AuthorDetail的情况
-前端传的数据:{"name":"张三","age":19,"author_detail":1}  -->基本不用
-前端传的数据:{"name":"三国演义","age":19,"addr":地址,"gender":1}  -->通常用它
-到了create中:validated_data 就是传入的{"name":"三国演义","age":19,"addr":地址,"gender":1} 
	{"name":"三国演义","age":19}  # 作者
     {"addr":地址,"gender":1}  #作者详情
      像上面那样进行处理就行
    
1.给的数据是 {"name":"K","age":19,"addr":"宁德","gender":1,"phone":155}   
2.写法总结
    2.1view.py和之前的都一样
        get--》取对象,扔到序列化类里,校验,返回Response
        POST-->把request.data,扔到序列化类里,校验,返回Response
    2.2模型类
    """class AuthorDetail(models.Model):
    gender=models.IntegerField(choices=((1,'男'),(2,'女'),(0,'未知')))
    phone=models.IntegerField()

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)
    author_detail = models.OneToOneField(to=AuthorDetail, on_delete=models.SET_NULL, null=True)
    def detail(self):
        return {"gender":self.author_detail.gender,"phone":self.author_detail.phone}"""
    
    	Author和AuthorDetail两个模型都要写,但是由于我们要用的是author所以把外键关系建在author上,然后由于要在author这张表上进行,也就是说要在author表上也取authordetail的数据,那么我们可以有三个方法
        -1source="AuthorDetail.xx",但是这样拿不全
        -2表模型写一个方法调用外表,拿数据,前提是外键要在author表
        	 def detail(self):
        		return {"gender":self.author_detail.gender,"phone":self.author_detail.phone}
        序列化类中要对应的也是detail字段--》detail=serializers.DictField(read_only=True)
        
        -3在序列化类中写方法
        	detail=serializers.SerializerMethodField(read_only=True)
            def get_detail(self,obj):
                return {"gender":obj.author_detail.gender,"phone":obj.author_detail.phone}
        -4子序列化就是在写author钱已经写了authordetail序列化类
        	author_detail=AuthorDetailSerializar()要和模型类里的外键字段名对应

6.3问题三

# 如果前端多传了数据:多了个addr,序列化类中没有
	-{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2],"addr":"上海"}
    多的不会被校验通过,就和form组件一样只有校验通过的数据
# 到了create中,validated_data中得数据:{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2]}

【七】ModelSerializer的使用

7.1前情提要

#1 之前写的序列化类,继承的serializer,跟表模型没有必然关系,所以要重写create和update方法
	-之前是不确定要保存到那个表里
    
#2  ModelSerializer,跟表模型一一对应
	-可以不重写,特殊情况需要重写 create和update
    -要序列化和反序列化的字段,可以通过表映射过来,也可以不写了

7.2基本使用

class BookSerialzier(serializers.ModelSerializer):
    class Meta:
        model=Book3
        fields='__all__'#也可以指定字段fields=['name','price']
#1.如果这些字段里有参数怎么传到里面
#         比如说
# name = serializers.CharField(max_length=8)
#         不好的点在于他没有提示,很容易打错
        extra_kwargs={'name':{'max_length':8},'price':{'max_value':200}}
#2.如果要写钩子函数写在哪--》要写在这最外面的一层,那个meta类你只能把他当作是写字段对应的地方
        # 局部钩子,一定要写在BookModelSerialzier 这一层
        def validate_name(self, name):
            return name
        # 同理,全局钩子一样
        # create和update 可以不用写了
7.3实现新增反序列化和查询序列化写法
1.序列化类中---》需要注意的是要写的字段在fields中都要注册,用四种方法取返回响应都可以
class BookModelSerialzier(serializers.ModelSerializer):
    class Meta:
        model = Book3
        fields = ['id','name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']  # 新增的publish_real 也要注册
        extra_kwargs = {'name': {'max_length': 8}, 'price': {'max_value': 100},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }

        def validate_name(self, name):
            if "sb" in name:
                raise ValidationError("名字中不能带有sb")

    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)
    
2.模型类
class Book3(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    # 多对多关系,需要有中间件表
    # 但是django的orm帮咱们优化了--》不用手动创建中间件,使用ManyToMany可以自动创建出来
    # authors 不是表里的字段,它是一个中间表
    authors=models.ManyToManyField(to='Author')
    def publish_detail(self):
        return {"name":self.publish.name,"addr":self.publish.addr}
    def author_list(self):
        l=[]
        obj=self.authors.all()
        for i in obj:
            l.append({'name':i.name,'age':i.age})
        return l

7.4什么时候需要重写create和update呢

"""当你需要在模型实例被创建或更新之前或之后执行特定逻辑时,就可能需要重写 create 和 update 方法。这包括但不限于自定义验证、设置字段值、处理关联对象或调用外部服务等"""
"""假设你有一个 Book 模型,它与 Author 模型通过 ForeignKey 关联。你可能希望在创建 Book 实例时自动将某个 Author 设置为作者,或者更新时验证某些条件"""

class BookSerializer(serializers.ModelSerializer):  
    class Meta:  
        model = Book  
        fields = ['title', 'author', 'publication_date']  
  
    def create(self, validated_data):  
        # 假设我们有一个特定的author_id需要设置,或者从请求中提取  
        # 这里只是作为示例,通常你会从validated_data中获取  
        author_id = self.context.get('author_id')  # 假设这是从视图中传递过来的  
        author = Author.objects.get(id=author_id)  
        validated_data['author'] = author  
  
        # 调用父类的create方法,传入经过修改的数据  
        return super().create(validated_data)
def update(self, instance, validated_data):  
    # 检查是否有特定的字段需要更新前的处理  
    if 'publication_date' in validated_data and validated_data['publication_date'] < some_date:  
        raise serializers.ValidationError("Publication date cannot be before a certain date.")  
  
    # 更新instance的字段  
    for field, value in validated_data.items():  
        setattr(instance, field, value)  
  
    # 调用父类的save方法保存更改  
    instance.save()  
  
    # 返回更新后的实例  
    return instance

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值