本文主要系统性的讲解django rest framwork 序列化组件的使用,基本看完可以解决工作中序列化90%的问题,写作参考官方文档https://www.django-rest-framework.org/api-guide/serializers/#modelserializer,分成如下九个部分:
01、为什么要用序列化组件
02、序列化组件的基本使用
03、序列化组件常用字段
04、序列化组件is_valid、validated_data
05、序列化组件校验字段
06、序列化组件.create() and .update()
07、序列化组件ModelSerializer
08、序列化组件构造复杂的结构
09、序列化组件修改返回值to_representation、to_internal_value
01、为什么要用序列化组件
我们知道前后端常用json数据结构交互, 在后端我们常想把一个对象返回给前端,但是json序列化是不能序列化对象(不过可以添加序列化参数encoder序列化原理和序列化组件差不多需要自己定义序列化类和返回的结构),所以就有了我们的序列化组件,可以自定义特定结构把对象序列化返回给前端,同时可以对前端传入的参数进行数据校验等功能。
02、序列化组件的基本使用
models
from django.db import models # Create your models here. class Book(models.Model): id = models.IntegerField(primary_key=True) title = models.CharField(max_length=255) desc = models.CharField(max_length=255) is_deleted = models.IntegerField(choices=[(1, "删除"), (0, "未删除")]) author = models.CharField(max_length=255)
serializer
from rest_framework.serializers import Serializer from rest_framework import serializers class BookSerializer(Serializer): id = serializers.IntegerField() title = serializers.CharField() desc = serializers.CharField() is_deleted = serializers.ChoiceField(choices=[(1, "删除"), (0, "未删除")], source="get_is_deleted_display") author = serializers.CharField()
views
from app01.models import Book from app01.serializer import BookSerializer from django.http import HttpResponse, JsonResponse # Create your views here. def get_books(request): books = Book.objects.all() se = BookSerializer(books, many=True) return JsonResponse(se.data, safe=False)
结果返回:
[{"id": 1, "title": "活着", "desc": "讲述一代人的人生", "is_deleted": "未删除", "author": "余华"}]
在写法上model和serializer的写法非常相近,但内在逻辑model是与数据库表的关系映射,serializer是对对象的序列化和反序列化。
03、序列化组件常用字段
常用字段类型
字段 | 字段构造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format='hex_verbose') format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol='both', unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
选项参数:
名称 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最大值 |
min_value | 最小值 |
通用参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
这一段引用自https://www.jianshu.com/p/26529651aa27
在这里额外讲一个参数source,在官方文档中是这样解释的:
将用于填充字段的属性的名称。可以是仅接受
self
参数的方法,例如URLField(source='get_absolute_url')
,也可以使用点分符号遍历属性,例如EmailField(source='user.email')
。当使用点分符号序列化字段时,default
如果在属性遍历期间任何对象不存在或为空,则可能需要提供一个值。该值
source='*'
具有特殊含义,用于指示应将整个对象传递给该字段。这对于创建嵌套表示或对需要访问完整对象才能确定输出表示的字段很有用。默认为字段名称。
其中比较常用的用法:
1、source="get_field_name_display"如上所展示,在choice字段中可以展示选项对应的解释,这中用法代表的是官方解释说的可以是仅接受self
参数的方法,也就是source中可以填serializer对象可以调用的方法(方法中需要传入self),内在逻辑是这个字段会展示此方法的返回的结果。
2、source='user.email'这种用法常在ModelSerializer的子类中,其中user为User对象,此中写法是指展示user的email属性,常用于我们想把外键对象user的属性和本对象的属性展示在同一层级,而不是下一级。
04、序列化组件is_valid、validated_data
当我们定义好序列化器时,怎么校验传入的字段呢?那就是is_valid方法,可以根据定义序列化器的校验规则判断传入字段是否合法。
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'}) serializer.is_valid() # False serializer.errors # {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
然后serializer.validated_data就可以获取校验过后的数据字典。
05、序列化组件校验字段
序列化组件校验字段的方式常有三种:
1、首先是在字段的validators属性, 其中传入一个校验方法列表如:validators=(my_validator, )其中my_validator中定义校验规则。
def multiple_of_ten(value): if value % 10 != 0: raise serializers.ValidationError('Not a multiple of ten') class GameRecord(serializers.Serializer): score = IntegerField(validators=[multiple_of_ten])
2、最常用的是定义一个validate_field_name(self, value)的函数(其中field_name指的是字段名),函数内是具体的逻辑。
from rest_framework import serializers class BlogPostSerializer(serializers.Serializer): title = serializers.CharField(max_length=100) content = serializers.CharField() def validate_title(self, value): """ Check that the blog post is about Django. """ if 'django' not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return value
3、最后一种是定义一个validate(self, data)其中data是所有字段的键值对,所以这个校验方法是对象级别的校验。
from rest_framework import serializers class EventSerializer(serializers.Serializer): description = serializers.CharField(max_length=100) start = serializers.DateTimeField() finish = serializers.DateTimeField() def validate(self, data): """ Check that start is before finish. """ if data['start'] > data['finish']: raise serializers.ValidationError("finish must occur after start") return data
当然或许当你看到这里会问why?how? 只能说源码是最好的答案。
06、序列化组件.create() and .update()
在我们定义的序列化类中, 可以添加create和update方法,当我们有需求是根据反序列化后的数据在数据库表中创建记录或者更新某条数据,这时我们就可以在create方法和update方法中定义对应的逻辑。
class CommentSerializer(serializers.Serializer): email = serializers.EmailField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField()
def create(self, validated_data): return Comment.objects.create(**validated_data) def update(self, instance, validated_data): instance.email = validated_data.get('email', instance.email) instance.content = validated_data.get('content', instance.content) instance.created = validated_data.get('created', instance.created) instance.save() return instance
接下来调用serializer.save()命令便可创建一条纪录或者更新一条记录,其中判断save时什么时候是创建什么时候是更新呢?关键在于serializer的实例化。
# .save() will create a new instance. serializer = CommentSerializer(data=data)
serializer.save()
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
serializer.save()
其中当Serializer类实例化没有传入model对象时会调用create方法创建一条记录, 如果Serializer类实例化时传入了model对象就会调用update方法更新一条记录。
有时除了反序列化的字段我们还需要其他字段怎么办呢?我们可以在save中传入参数名和值,可以在validated_data中根据参数名取到对应的值。
serializer.save(owner=request.user)
这样我们就可以在validated_data.get("owner")就可以取到user对象了。
07、序列化组件ModelSerializer
ModelSerializer和表单的ModelForm组件很相似,都极大简化了我们的开发,可以在内部类Meta中定义对应的model,ModelSerializer就会自动生成model字段对应的Field不用我们定义。
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
其中fields中定义序列化的字段,如果是全部字段就写__all__, 上述例子我们没有定义具体的字段,ModelSerializer帮我们自动生成了 'id', 'account_name', 'users', 'created'的Field。
08、序列化组件构造复杂的结构
以下是个存在一对多和多对多字段的序列化器
serializer
from res_framework import serializers #这个类用于被实例化,多对多字段这么写 class AuthorSerializer(serializers.Serializer): id = serializers.Charfield() name = serializers.Charfield() age = serializers.Charfield() #传给views.py的主类 class BookSerializer(serializers.Serializer): name = serializers.Charfield() #source 可以指定字段 , id是要序列化的表名。 id = serializers.CharField(source='nid') #,source后字段用.的方式可以跨表查询。 publish = serializer.CharField(source='publish.email') ''' 如果在models.py的book类中定义一个test方法。 def test(self): return str(self.price)+self.name ''' # 返回的结果就会有xx字段,souce不但可以指定表模型字段,还可以指定模型表方法,并且赋值给xx变量 xx = serializers.Charfield(source='test') #外键的实现方法: #一对多字段 #如果要通过外键字段返回出版社的所有信息,包括id,name,email... #obj是当前循环序列化到的数据对象 publish = serializers.SerializerMethodField() def get_publish(self,obj): return {’id‘:obj.publish.pk,'name':obj.publish.name} #多对多字段 #所有作者的详情,也展示出来 authors = serializers.SerializermethodFiled() def get_authors(self,obj): author_list = obj.authors.all() author_ser = AuthorSerializer(author_list,many=True) return author_ser.data
views
from rest_framework.views import APIView from rest_framework.response import Response from app01 import models class BookView(APIview): def get(self,request,*args,**kwargs): #获取所有图书数据 response = {'status':100,'msg':'获取成功'} book_list = models.Book.objects.all() #实例化BookSerializer类,把要序列化的数据book_list传入 #如果要序列化querySet对象,一定要加many = True book_ser = BookSerializer(book_list,many=True) #把序列化后的数据book_ser.data 拿出来放到response字典中返回给客户端 response['data'] = book_ser.data return Response(response)
如果使用ModelSerializer:
serializer
from app01 import models class PublishSerializer(serializers.ModelSerializer): class Meta: #固定写法 # 指定要序列化Book表 model = models.Book #指定要序列化的字段 fields = ['nid','name'] #序列化所有字段 fileds ='__all__‘ #要排除的字段(不能与fileds连用) # exclude = ['name','price'] #深度判定 depth = 1 #如果要不按照父类的来,想要自己定义显示的字段的话,自己定义一个,覆盖掉父类的字段属性。 publish = serializers.SerializerMethodField() #一对多字段 def get_publish(self,obj): return {’id‘:obj.publish.pk,'name':obj.publish.name}
此段摘自https://blog.csdn.net/qq_36019490/article/details/90339490
这里额外解释一下depth这个参数:在对象外键另一对象,另一对象又外键另一对象以此类推,depth的作用就是可以决定序列化时的外键深度。
复杂序列化器的要点在于,serializers.SerializerMethodField()和get_field_name的使用获取自己想得到的字段值,还有source的使用,上面有讲。
09、序列化组件修改返回值to_representation、to_internal_value
to_representation(self, instance):如果序列化器定义了此方法,可以改变序列化对象data的值,也就是serializer.data的值,你可以根据自己的业务场景去重新构造返回值。
def to_representation(self, instance): """Convert `username` to lowercase.""" ret = super().to_representation(instance) ret['username'] = ret['username'].lower() return ret
to_internal_value(self, data): data为未经校验的数据字段, 此方法可以实现校验和修改反序列化后的值,然后返回。如果不想修改反序列化后的值只是做校验的话,完全可以使用validate方法替代。
def to_internal_value(self, value): if value == None: return 0 return value
总而言之,这两个方法一个是用于重新构造validated_data并返回,一个用于重新构造serializer.data的值并返回。