前戏
在之前我们写序列化器的时候,写的很low,遇到反序列化的有时候还需要重写该字段,用post请求的时候,还要重写create方法,用put请求的时候,还需要重写update方法。总而言之,写起来很麻烦。来看看之前的是怎么写的
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # 只序列化,不走校验 title = serializers.CharField(max_length=32, validators=[my_validate]) pub_time = serializers.DateField() category = serializers.CharField(source="get_category_display", read_only=True) # 只序列化用 # 因为前端传的是数字,所以需要重写 post_category = serializers.IntegerField(write_only=True) # 只反序列化用 publisher = PublisherSerializer(read_only=True) # 一对多的表 只序列化用 authors = AuthorSerializer(many=True, read_only=True) # 多对多的表需要指定many=True 只序列化用 publisher_id = serializers.IntegerField(write_only=True) # 只反序列化用 author_list = serializers.ListField(write_only=True) # 只反序列化用 def create(self, validated_data): # validated_data校验通过的数据 # 通过ORM操作给book表增加数据 book_obj = Book.objects.create(title=validated_data['title'], pub_time=validated_data['pub_time'], category=validated_data['post_category'], publisher_id=validated_data['publisher_id']) book_obj.authors.add(*validated_data['author_list']) # 这个参数可能是一个列表 return book_obj def update(self, instance, validated_data): # instance 更新的book_obj对象 # validated_data 校验通过的数据 instance.title = validated_data.get("title",instance.title) instance.pub_time = validated_data.get("pub_time",instance.pub_time) instance.category = validated_data.get("post_category",instance.category) instance.publisher_id = validated_data.get("publisher_id",instance.publisher_id) if validated_data.get("author_list"): # 可能有多个值 instance.author.set(validated_data["author_list"]) instance.save() # 保存 return instance def validate_title(self, value): # 对单一字段校验 if "BDYJY" not in value.upper(): return value raise serializers.ValidationError('标题里含有非法字符') # 抛出错误 def validate(self, attrs): # 对多个字段校验 # attrs是一个字典,里面是传过来的所有字段 if 'python' in attrs['title'].lower() and attrs['post_category']==1: return attrs else: raise serializers.ValidationError('传的参数有误,请重新上传')
from django.db import models # Create your models here. __all__ = ["Book", "Publisher", "Author"] class Book(models.Model): title = models.CharField(max_length=32) CHOICES = ((1, "python"), (2, "Liunux"), (3, "Go")) category = models.IntegerField(choices=CHOICES) pub_time = models.DateField() publisher = models.ForeignKey(to="Publisher") authors = models.ManyToManyField(to="Author") class Publisher(models.Model): title = models.CharField(max_length=32) def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=32) def __str__(self): return self.name
ModelSerializer
既然上面的写法很low,DRF提供给了我们ModelSerializer,之前序列化器继承的是serializers.Serializer,现在要继承serializers.ModelSerializer
把之前的BookSerializer类里的东西删掉重写
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book # 对应的表名 fields = "__all__" # 显示所有的字段 # fields = ['id','title'] # 显示指定的字段 # exclude = ['id', 'title'] # 不显示指定的字段
这样我们访问这个接口查看数据
会发现Choise字段和关联表的字段不是我们想要的,我们可以加上depth来让它显示成为我们想要的值,后面的值是表示几层,因为book表和author,publisher表都是一层关联,另外两者表没有一对多和多对一的关系,所以我们可以写成depth=1
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book # 对应的表名 fields = "__all__" # 显示所有的字段 # fields = ['id','title'] # 显示指定的字段 # exclude = ['id', 'title'] # 不显示指定的字段 depth = 1 # 会让你这些所有的外键关系变成read_only = True
在来请求下这个接口
这样我们虽然解决了一对一和多对多的字段,但是Chiose字段还不是显示我们想要的数据,而且depth会让你这些所有的外键关系变成read_only = True,所以实际开发中都不用,都是重写
重写字段
class BookSerializer(serializers.ModelSerializer): # 重写publisher字段 publisher_info = serializers.SerializerMethodField() author_info = serializers.SerializerMethodField() def get_publisher_info(self, obj): # 函数名为 get_自定义的字段名 # return了一个'ok',表示上面的publisher_info='ok',会返回给前端 return 'ok' def get_author_info(self, obj): return 'yes' class Meta: model = Book # 对应的表名 fields = "__all__" # 显示所有的字段 # fields = ['id','title'] # 显示指定的字段 # exclude = ['id', 'title'] # 不显示指定的字段
其中的obj就是我们在views.py里序列化的每个对象,也就是下面的book_queryset
ser_obj = BookSerializer(book_queryset, many=True)
请求接口
会发现我们重写的数据都返回给了调用这个接口的,所以我们可以自定义需要返回哪些字段。
class BookSerializer(serializers.ModelSerializer): # 重写publisher字段 publisher_info = serializers.SerializerMethodField() author_info = serializers.SerializerMethodField() category_display = serializers.SerializerMethodField() def get_publisher_info(self, obj): # 函数名为 get_自定义的字段名 publisher_obj = obj.publisher ForeignKey,拿到的是publisher表的对象 return {"id":publisher_obj.id,'title':publisher_obj.title} def get_author_info(self, obj): authors_queryset = obj.authors.all() ManyToMany,.all拿到的是queryset里所有的对象 return [{'id':author.id,'name':author.name} for author in authors_queryset] def get_category_display(self, obj): return obj.get_category_display() # 调用这个方法,会返回汉字 class Meta: model = Book # 对应的表名 fields = "__all__" # 显示所有的字段 # fields = ['id','title'] # 显示指定的字段 # exclude = ['id', 'title'] # 不显示指定的字段
publisher_info = serializers.SerializerMethodField() 叫做方法字段,需要一个方法,把方法里的返回值赋值给该字段
上面我们自定义的字段已经显示了,然而之前的字段我们可以让它不显示,在Meta加个 extra_kwargs ,里面的是个字典,key为不显示的字段名,value如果是write_only为True,就不显示了
class BookSerializer(serializers.ModelSerializer): # 重写publisher字段 publisher_info = serializers.SerializerMethodField(read_only=True) author_info = serializers.SerializerMethodField(read_only=True) category_display = serializers.SerializerMethodField(read_only=True) # Choise字段,后面必须是display def get_publisher_info(self, obj): # 函数名为 get_自定义的字段名 publisher_obj = obj.publisher return {"id":publisher_obj.id,'title':publisher_obj.title} def get_author_info(self, obj): authors_queryset = obj.authors.all() return [{'id':author.id,'name':author.name} for author in authors_queryset] def get_category_display(self, obj): return obj.get_category_display() class Meta: model = Book # 对应的表名 fields = "__all__" # 显示所有的字段 # fields = ['id','title'] # 显示指定的字段 # exclude = ['id', 'title'] # 不显示指定的字段 extra_kwargs = {'category':{"write_only":True},'publisher':{"write_only":True}, 'authors':{"write_only":True}}
这样,当我们使用post或者put方法时,就不需要再重写create方法和update方法了
在函数里重写方法字段时,里面写了read_only=True,表示序列化时使用,而在Meta类里,默认的字段category,publisher,author为write_only,所以序列化的时候,这些字段不显示,显示的是重写的那些字段。反序列化时用的是extra_kwargs里的字段
总结
ModelSerializer
默认生成关联的模型表里的所有字段
配置
class Meta:
model=表名
fields="__all__"/["字段名",]
exclude=["字段名",]
depth=1 外键关系找一层 会让外键关系字段变成read_only=True
extra_kwargs={"字段名": {配置的属性}}
SerializerMethodField()
方法字段 会调用它自己的钩子方法,把方法的返回值给字段
def get_字段名(self, obj):
循环序列化的每个模型对象就是obj
对obj进行ORM操作
return 自定义的数据结构