【一】序列化器-Serializer
作用:
序列化:序列化器会把模型对象转换成字典,经过Response以后变成json字符串 反序列化:把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型 反序列化校验:完成数据校验功能
【1】序列化类快速使用(查看)
创建一个数据库模型类,并迁移数据库makemigrations,migrate
from django.db import models # Create your models here. class Student(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() gender = models.IntegerField(choices=((0, '未知'), (1, '男'), (2, '女')))
创建serializer.py文件(序列化器)
from rest_framework import serializers # 使用步骤: # 1 定义一个类,继承Serializer # 2 在类中写要序列化的字段,这个字段和models中的字段一一对应,且不想显示哪一个字段就注释哪一个 # 3 在视图类中使用 class StudentSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) name = serializers.CharField(required=True) age = serializers.IntegerField(required=True) gender = serializers.IntegerField(required=False, default=0)
在views.py文件中使用序列化类
from rest_framework.views import APIView from rest_framework.response import Response from django.views import View from .models import Student from .serializer import StudentSerializer class StudentView(APIView): # 查看所有学生 def get(self, request): res = Student.objects.all() # 方法一:麻烦 代码多 # student = [] # for l in res: # student.append({'name': l.name, 'age': l.age, 'gender': l.get_gender_display()}) # 方法二:drf提供的序列化类 # 使用序列化类完成序列化 :instance 是要序列化的对象,如果是多条 many=True serializer = StudentSerializer(instance=res, many=True) # 如果是当个数据是data是字典,多条数据时他是列表套字典 print(serializer.data) return Response(serializer.data) class StudentViews(APIView): # 根据id查看对象 def get(self, request, pk): student = Student.objects.filter(pk=pk).first() # 序列化 serializer = StudentSerializer(instance=student) # 返回信息 return Response(serializer.data)
【2】常用字段类
# 1 记住--》跟models中得 model.字段类, 一一对应 # 2 具体有哪些--带#的都是常用的需要记住 # 3 绝招:如果不知道models和Serializer如何对应 序列化类统一用 CharField DateTimeField # DateField # TimeField # IntegerField # CharField # BooleanField NullBooleanField EmailField RegexField SlugField URLField UUIDField IPAddressField FloatField DecimalField ChoiceField MultipleChoiceField FileField ImageField # --------两个特殊的,很重要---- ListField DictField
【3】常用字段参数
# 1 序列化的 字段类在实例化时,传参数 -作用是:为了反序列化校验用 # 2 有哪些字段参数--》了解 # 只针对于:CharField max_length 最大长度 min_lenght 最小长度 allow_blank 是否允许为空 trim_whitespace 是否截断空白字符 # 只针对于:IntegerField max_value 最小值 min_value 最大值 # 只针对于:DateTimeField format:序列化时,显示的格式 DateTimeField(format='Y-m-d') # 所有字段类都用 required:是否必填 default :反序列化时使用的默认值 allow_null 表明该字段是否允许传入None,默认False # 两个重点 read_only 表明该字段仅用于序列化输出,默认False write_only 表明该字段仅用于反序列化输入,默认False
【4】反序列化保存(新增、修改、删除)
# 新增 # 检查传入的数据是否符合定义的字段类型和约束条件 1 写个序列化类,继承Serializer 2 在序列化类中写字段,和字段参数[校验用的] 3 在视图类中使 # viws.py def post(self, request): serializer = StudentSerializer(data=request.data) # 判断方法一:如果校验不通过,直接抛出异常,后面代码就不会执行了 serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "添加成功"}) # 判断方法二:if判断方法 # # 检查传入的数据是否符合定义的字段类型和约束条件 # if serializer.is_valid(): # # 校验通过 保存,需要在serializer文件中重写create方法 # serializer.save() # # 返回新增的的数据 # return Response(serializer.data) # else: # # 校验失败返回到前端 # return Response({"code": 301, "msg": serializer.errors}) # serializer.py # validated_data是前端传入的,然后在views中用is_valid()校验过的数据 def create(self, validated_data): # 这里使用**validated_data传入的数据如果比表的字段多或 与表的字段不对应 是不行的 # 如果少的话取决于少的字段是否可以为空 student = Student.objects.create(**validated_data) return student # 这里需要返回student新增对象 后面增加完成后需要输出serializer.data使用 # 修改 # views.py def put(self, request, pk): # 找到要修改的学生对象 student = Student.objects.filter(pk=pk).first() # 拿到前端传入的数据 要修改的instance 和 修改后的data serializer = StudentSerializer(instance=student, data=request.data) # 检查传入的数据是否符合定义的字段类型和约束条件 if serializer.is_valid(): serializer.save() return Response(serializer.data) # return Response({"code": 200, "msg": "修改成功"}) else: return Response({"code": 302, "msg": "修改失败"}) # serializer.py def update(self, instance, validated_data): # instance 需要修改的对象 # validated_data 前端传入的修改后的数据 # 方法一: # instance.name = validated_data.get('name') # instance.age = validated_data.get('age') # instance.gender = validated_data.get('gender') # 方法二: # # 循环拿到字典的一个个key for key in validated_data: # 下面这句话等同于 instance.name = validated_data.get('name') setattr(instance, key, validated_data.get(key)) instance.save() # 将修改后的对象返回出去 return instance # 删除 def delete(self, request, pk): Student.objects.filter(pk=pk).delete() return Response({"code": 200, "msg": "删除成功"})
【5】看源码
# 1 为什么要重写 update和create 因为执行serializer.save--->会触发self.create或self.update执行 我们没写--》找父亲--》找父亲--》找到了save def create(self, validated_data): raise NotImplementedError('`create()` must be implemented.') # 2 为什么调用的都是save,但是最后触发的序列化类中得方法是不同的? 因为内部判断了 instance 是否为None # 阅读源码 -serializer.save()-->序列化类对象【自己写的StudentSerializer,没有save】 调用save,会触发父类的save--》Serializer中也没有save---》继续找父类BaseSerializer 有save方法 -save源码 # self 是咱们写的序列化类的对象 # 如果是修改,instance 有值,没有值的情况就是添加 if self.instance is not None: # self.update 是重写,在这之前里面是有数据的有值的 self.instance = self.update(self.instance, validated_data) else: self.instance = self.create(validated_data) return self.instance
【6】反序列化校验
(1)第一层:字段自己的校验
-
写在字段类的属性上
# serializer.py from rest_framework import serializers class StudentSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) name = serializers.CharField(required=True, max_length=6, min_length=2) age = serializers.IntegerField(required=True, max_value=30, min_value=2) gender = serializers.IntegerField(required=False, default=0,)
-
补充方法(了解即可)
-
在字段上使用 validators=[函数内存地址]
-
# 示例 name = serializers.CharField(max_length=6, min_length=2,validators=[函数内存地址])
【tips】:postman上报错都是英文的,将他调成中文
-
settings.py注册app
'rest_framework'
-
修改参数
# LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-hans' # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_TZ = False
(2)局部钩子单独控制某个字段
# validate_字段名(self,字段名) def validate_name(self, name): if name.startswith("sb"): # 校验失败抛出异常 raise ValidationError("姓名不能以sb开头") else: # 校验成功,返回name参数 return name
(3)全局钩子,不是单独校验某个字段的,用来校验多个字段
def validate(self, attrs): # attrs 是前端传入 通过自己设置的 字段要求 和 局部钩子校验后的数据 age = attrs.get("age") name = attrs.get("name") if str(age) == name: raise ValidationError("此二值不可以一致") else: return attrs
【7】序列化之source(定制返回方式一,不常用)
-
在serializer.py文件中使用
# 使用方法一: # 将原来的字段名name修改成user 修改后的字段名 = serializers.CharField(required=True, max_length=6, min_length=2, source='表中原来的字段名') user = serializers.CharField(required=True, max_length=6, min_length=2, source='name') # name = serializers.CharField(required=True, max_length=6, min_length=2) # 使用方法二: # 在模型表models.py @property def new_name(self): return self.name + "_nb" # 在序列化类中 user = serializers.CharField(required=True, max_length=6, min_length=2, source='new_name') # 结果:将原来字段name数据的后面加_nb将字段名修改成user # 【补充】 # 这样也是可以的 new_name = serializers.CharField(required=True, max_length=6, min_length=2) # 使用方法三: # 使用source定制,跨表查询定制 user = serializers.CharField(required=True, max_length=6, min_length=2, source='publish.name')
【8】序列化之定制返回格式(使用book表做示例)
-
创建模型表 models.py
# book数据表 class Book(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, "city": self.publish.city, "addr": self.publish.addr} class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) addr = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() addr = models.CharField(max_length=32)
-
views.py
class BookView(APIView): def get(self, request): books = Book.objects.all() serializer = BookSerializer(books, many=True) return Response({"code": 200, "msg": "请求成功", "data": serializer.data})
-
serializer.py
# # 子序列话方法,可以自己选择需要输出的字段 class PublishSerializer(serializers.Serializer): name = serializers.CharField() addr = serializers.CharField() city = serializers.CharField() # 子序列话方法,可以自己选择需要输出的字段 class AuthorSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField() age = serializers.IntegerField() addr = serializers.CharField() # ListField # DictField class BookSerializer(serializers.Serializer): name = serializers.CharField() price = serializers.IntegerField() # 定制返回格式方式一:使用source (用的少) publish = serializers.CharField(source="publish.name") # 定制返回格式方式二:在表模型中写方法 publish_detail = serializers.DictField() # 定制返回格式方式三:在序列化类中写方法 get_字段名 publish = serializers.SerializerMethodField() # def get_publish(self, obj): # # 这里的obj就是当前序列化得到的book return {"name": obj.publish.name, "city": obj.publish.city, "addr": obj.publish.addr} # 定制返回格式方式四:使用子序列化 当前序列化得到book对象 book.publish --> # 出版社对象 --》再使用PublishSerializer序列化类做序列化对象 publish = PublishSerializer() # 【练习】通过上面的几种方法进行作者的多对多序列化 # 练习1:在表模型中写方法 author = serializers.ListField(source="author_list") # 练习2:在序列化类中写方法 author_all = serializers.SerializerMethodField() def get_author_all(self, obj): author_all = obj.author.all() author_list = [] for author in author_all: author_list.append({"name": author.name, "age": author.age}) return author_list # 练习3:子序列化 因为作者是多个所以要添加many=True # 这里authors必须和模型表中的字段名一致(可以用source对字段名进行定制) authors = AuthorSerializer(many=True)
【9】多表关联的反序列化
-
serializer.py
class BookSerializer(serializers.Serializer): # 序列化查看和反序列化新增都需要用到 name = serializers.CharField() price = serializers.IntegerField() # 只有在序列化查看的时候才用到,可以添加一个只读属性read_only=True publish_detail = 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) # 重写create方法 def create(self, validated_data): # validated_data = {"name":"云边","price":30,"publish_id":2,"authors":[1,2]} # 弹出作者"authors":[1,2] author = validated_data.pop("authors") book = Book.objects.create(**validated_data) # 作者 保存到 中间表中--->多对多关系,使用ManyToMany自动创建中间件,要借助于 ManyToMany 字段操作中间表 # .clear()清空、.add()添加、.remove()删除 book.authors.add(*author) return book
-
views.py (book视图类)
def post(self, request): serializer = BookSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "添加成功", "data": serializer.data})
问题一:
# 在增加图书的时候,也增加出版社和作者--》肯定不用 -前端传的数据:{"name":"三国演义","price":19,"publish":{"name":"上海出版社","addr":"上海"},"authors":[{},{}]} -到了create中:validated_data 就是传入的
问题二:
# 针对于 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} #作者详情
问题三:
# 如果前端多传了数据:多了个addr,序列化类中没有 -{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2],"addr":"上海"} # 到了create中,validated_data中得数据:{"name":"三国演义","price":19,"publish_id":1,"authors":[1,2]}
【10】完善查询单个、修改、删除 接口
-
serializer.py
-
重写修改
def update(self, instance, validated_data): instance.name = validated_data.get("name") instance.price = validated_data.get("price") instance.publish_id = validated_data.get("publish_id") author = validated_data.pop("authors") instance.authors.set(*author) return instance
-
views.py(视图类)
class BookViews(APIView): def get(self, request, pk): book = Book.objects.filter(pk=pk).first() serializer = BookSerializer(instance=book) return Response({"code": 200, "msg": "查询成功", "data": serializer.data}) def put(self, request, pk): book = Book.objects.filter(pk=pk).first() serializer = BookSerializer(instance=book, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({"code": 200, "msg": "修改成功", "data": serializer.data}) def delete(self, request, pk): Book.objects.filter(pk=pk).delete() return Response({"code": 200, "msg": "删除成功"})
【11】ModelSerializer使用
-
对比
# 之前写的序列化类,继承Serializer,跟表模型没有必然联系,才需要重写 create和update -确定保存或修改到哪个表 # ModelSerializer,跟表模型一一对应, -可以不重写,特殊情况需要重写 create和update -要序列化和反序列化的字段,可以通过表映射过来,也可以不写
-
基于ModelSerializer编写Book的序列化和反序列化
class BookModelSerializer(serializers.ModelSerializer): class Meta: model = Book # fields = "__all__" # 表示book表中所有字段都在这写了一遍 fields = ["name", "price", "publish", "authors", "publish_detail", "author_list"] # 只要book表中的name和price # 字段参数限制 下面的代码相当于 name = serializers.CharField(max_length=8) extra_kwargs = {"name": {"max_length": 8}, "price": {"max_value": 1000}, "publish": {"write_only": True}, "authors": {"write_only": True}, "publish_detail": {"read_only": True}, "author_list": {"read_only": True}, } # 序列类中写方法 class BookModelSerializer(serializers.ModelSerializer): class Meta: model = Book # fields = "__all__" # 表示book表中所有字段都在这写了一遍 fields = ["name", "price", "publish", "authors", "publish_real", "author_all"] # 只要book表中的name和price # 字段参数限制 下面的代码相当于 name = serializers.CharField(max_length=8) extra_kwargs = {"name": {"max_length": 8}, "price": {"max_value": 1000}, "publish": {"write_only": True}, "authors": {"write_only": True} } publish_real = serializers.SerializerMethodField() def get_publish_real(self, obj): return {"name": obj.publish.name, "city": obj.publish.city, "addr": obj.publish.addr} author_all = serializers.SerializerMethodField() def get_author_all(self, obj): author_all = obj.authors.all() author_list = [] for author in author_all: author_list.append({"name": author.name, "age": author.age}) return author_list # 局部钩子用法 def validate_name(self, name): if "sb" in name: raise serializers.ValidationError("名字不合法") else: return name # 全局钩子用法 def validate(self, attrs): name = attrs.get("name") price = attrs.get("price") if name == str(price): raise serializers.ValidationError("不合法") else: return attrs