简介 使用序列化有四种方式
使用json模块,完全手写
使用django自带的序列化模块 1,# from django.core import serializers 2,# data=serializers.serialize(“json”,book_list)
使用REST framework 带的序列化方法,但是自己写规则 BookSerializers(serializers.Serializer)
使用REST framework 带的序列化方法,不自定义,完全使用模块 BookSerializers(serializers.ModelSerializer)
开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json
之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。
简单示例 models部分: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from django.db import modelsclass Book (models.Model) : title=models.CharField(max_length=32 ) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish" ) authors=models.ManyToManyField("Author" ) def __str__ (self) : return self.titleclass Publish (models.Model) : name=models.CharField(max_length=32 ) email=models.EmailField() def __str__ (self) : return self.nameclass Author (models.Model) : name=models.CharField(max_length=32 ) age=models.IntegerField() def __str__ (self) : return self.name
views部分: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from rest_framework.views import APIViewfrom rest_framework.response import Responseclass BookViewSet (APIView) : def get (self,request,*args,**kwargs) : book_list=Book.objects.all() bs=BookSerializers(book_list,many=True ) return Response(bs.data) bs=BookSerializers(book_list,many=True ) return Response(bs.data)
方式三:BookSerializers 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from django.shortcuts import HttpResponsefrom django.core import serializersfrom rest_framework import serializersclass BookSerializers (serializers.Serializer) : title=serializers.CharField(max_length=32 ) price=serializers.IntegerField() pub_date=serializers.DateField() publish=serializers.CharField(source="publish.name" ) authors=serializers.SerializerMethodField() def get_authors (self,obj) : temp=[] for author in obj.authors.all(): temp.append(author.name) return temp class Meta : model=Book fields=['title' ,'price' ,'pub_date' ,'authors' ] def validate_title (self, value) : if '草' in value: raise serializers.ValidationError('不符合社会主义核心价值观' ) return value
方式四:ModelSerializer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class BookSerializers (serializers.ModelSerializer) : class Meta : model = models.Book fields = ['nid' ,'name' ] fileds ='__all__' depth = 1 publish = serializers.SerializerMethodField() def get_publish (self,obj) : return {'id' :obj.publish.pk,'name' :obj.publish.name}
序列化组件常用字段 常用字段类型
字段
字段构造方式
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页面时,显示的字段帮助提示信息
source 在官方文档中是这样解释的:
将用于填充字段的属性的名称。可以是仅接受self
参数的方法,例如URLField(source='get_absolute_url')
,也可以使用点分符号遍历属性,例如EmailField(source='user.email')
。当使用点分符号序列化字段时,default
如果在属性遍历期间任何对象不存在或为空,则可能需要提供一个值。 该值source='*'
具有特殊含义,用于指示应将整个对象传递给该字段。这对于创建嵌套表示或对需要访问完整对象才能确定输出表示的字段很有用。 默认为字段名称。
其中比较常用的用法:
source=”get_field_name_display”如上所展示,在choice字段中可以展示选项对应的解释,这中用法代表的是官方解释说的可以是仅接受self
参数的方法,也就是source中可以填serializer对象可以调用的方法(方法中需要传入self),内在逻辑是这个字段会展示此方法的返回的结果。
source=’user.email’这种用法常在ModelSerializer的子类中,其中user为User对象,此中写法是指展示user的email属性,常用于我们想把外键对象user的属性和本对象的属性展示在同一层级,而不是下一级。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class UserSerializer(serializers.Serializer): type = serializers.IntegerField(source ='user_type' ) user_type = serializers.CharField(source ='get_user_type_display' ) # choices字段显示 username = serializers.CharField() pwd = serializers.CharField(source ='password' ) # 自定义serializer中的key值 group_title = serializers.CharField(source ='group.title' ) # 关联对象属性 roles = serializers.CharField(source ='roles.all' ) # 多对多关系 roles_info = serializers.SerializerMethodField() # 表示自定义方法,显示querytset对象详情 def get_roles_info(self, row): roles = row.roles.all() ret = [] for item in roles: ret.append( { 'id' : item.id, 'title' : item.title } ) return ret
如果没有指定在Filed中没有定义source参数的时候,就自动与数据库modles定义的字段进行匹配,如上面的userrname字段。在定义字段后,Serializer类中可以自定义属性如type
当models中是以choice定义时:需要定义source参数定义get_字段名_display才能获取数据,这与在模板语言中的用法一样,如上面的user_type
外键关联的时候,直接 外键字段名.属性 的方式定义传参给source参数即可,如上面的group.title
对于roles字段,想直接获取所有的对象,但是无法做到细粒度的将对象的所有属性展示出来,只能获取到QuerySet对象
自定义字段,处理数据,如roles_info获取所有的role对象的属性,处理数据可以定义方法,方法名格式为get_属性,并return值最终返回值
serializers.Serializer序列化 数据模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from django.db import models__all__ = ['Book' , 'Publisher' , 'Author' ]class Book (models .Model ): title = models.CharField (max_length =32) CHOICES = ( (1, 'Python '), (2, 'Linux '), (3, 'Go ') ) category = models.IntegerField (choices =CHOICES ) pub_time = models.DateField () publisher = models.ForeignKey (to = 'Publisher ', on_delete =models .CASCADE ) authors = models.ManyToManyField (to = 'Author ') class Publisher (models .Model ): title = models.CharField (max_length =32) class Author (models .Model ): name = models.CharField (max_length =32)
创建序列化器 在app目录下创建serializers.py文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 from rest_framework import serializersfrom .models import Book class PublisherSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length =32) class AutherSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length =32) class BookSerializer(serializers.Serializer): # required =False 指明反序时(前端提交数据过来)不做校验 id = serializers.IntegerField(required =False ) title = serializers.CharField(max_length =32) pub_time = serializers.DateField() "" " # source 比较常用的用法: 1、source=" get_field_name_display",在choice字段中可以展示选项对应的解释,这中用法代表的是官方解释说的可以是仅接受self参数的方法,也就是source中可以填serializer对象可以调用的方法(方法中需要传入self),内在逻辑是这个字段会展示此方法的返回的结果。 2、source='user.email'这种用法常在ModelSerializer的子类中,其中user为User对象,此中写法是指展示user的email属性,常用于我们想把外键对象user的属性和本对象的属性展示在同一层级,而不是下一级。 " "" category = serializers.CharField(source ='get_category_display' , read_only =True ) # 外键关系的序列化是嵌套的序列化对象;内部通过外键关系的id找到publisher对象,再使用PublisherSerializer(publisher对象) publisher = PublisherSerializer(read_only =True ) # 嵌套序列化器 authors = AutherSerializer(many =True , read_only =True ) # 多对多关系需要加上参数:many = True # read_only:指明该字段只做序列化,反序列化时不走该字段;那么反序列化时就必须重新指定字段名 # write_only:指明该字段只做反序列化,不做序列化,也就是从数据库读出来的数据,序列化返的时候,不显示某个字段 post_category = serializers.IntegerField(write_only =True ) publisher_id = serializers.IntegerField(write_only =True ) author_list = serializers.ListField(write_only =True ) authors =serializers.SerializerMethodField() # 多对多时,自定义字段字段 def get_authors(self,obj): # 名字固定写法 temp=[] for author in obj.authors.all(): temp.append(author.name) return temp def create(self, validated_data): # 调用 save 时执行 # validated_data 校验通过的数据 # 通过ORM操作给Book表添加数据 # {'title' : 'Die Hard' , 'pub_time' : datetime.date(2020, 1, 12), 'post_category' : 1, 'publisher_id' : 1, 'author_list' : [1, 2]} 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): # 调用 save 时执行 # instance为需要更新的对象 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.authors.set (validated_data['author_list' ]) instance.save() return instance
序列化组件is_valid、validated_data 当我们定义好序列化器时,怎么校验传入的字段呢?那就是is_valid方法,可以根据定义序列化器的校验规则判断传入字段是否合法。
1 2 3 4 5 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就可以获取校验过后的数据字典。
序列化组件校验字段 序列化组件校验字段的方式常有三种:
1、首先是在字段的validators属性, 其中传入一个校验方法列表如:validators=(my_validator, )其中my_validator中定义校验规则。
1 2 3 4 5 6 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指的是字段名),函数内是具体的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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. 单个字段校验;value就是title的值 """ if 'django' not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return value
3、最后一种是定义一个validate(self, data)其中data是所有字段的键值对,所以这个校验方法是对象级别的校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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. 多个字段校验;attrs是个字典,包含前端传递过来的所有字段 """ if data['start'] > data['finish']: raise serializers.ValidationError("finish must occur after start") return data
当然或许当你看到这里会问why?how? 只能说源码是最好的答案。
validate做数据校验错误返回的key问题: 1 2 3 4 5 6 7 8 9 10 from rest_framework import serializersdef validate (self, attrs) : password = attrs.get('password' ) re_password = attrs.pop('re_password' ) print(password, re_password) if password != re_password: raise serializers.ValidationError('两次密码不一致' ) return attrs
校验如有错误,报错的格式如下:
1 2 3 4 5 6 7 8 { "status" : 1 , "message" : { "non_field_errors" : [ "两次密码不一致" ] } }
message的key为non_field_errors,传递到前端不方便处理,可以自定义一个key
1 raise serializers.ValidationError({'password' : '两次密码不一致' })
报错格式如下:
1 2 3 4 5 6 7 8 { "status" : 1 , "message" : { "password" : [ "两次密码不一致" ] } }
类视图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class BookView(APIView) : def get(self, request): book_queryset = Book . objects.all() ser_obj = BookSerializer(book_queryset , many = True) return Response(ser_obj .data ) def post(self, request): book_obj = request.data # print(book_obj, type (book_obj)) ser_obj = BookSerializer(data = book_obj ) if ser_obj.is_valid() : # ser_obj.save() 使用该方法时,序列化器中必须实例create() 方法 ser_obj.save() return Response(ser_obj .validated_data ) return Response(ser_obj .errors ) class BookEditView(APIView) : def get(self, request, id): book_obj = Book . objects.filter(id = id).first() ser_obj = BookSerializer(book_obj ) return Response(ser_obj .data ) def put(self, request, id): book_obj = Book . objects.filter(id=id).first() # partial=True 指明为部分字段做校验,即传入什么字段就校验什么字段,其他字段不做校验 ser_obj = BookSerializer(instance = book_obj , data = request .data , partial = True) if ser_obj.is_valid() : ser_obj.save() return Response(ser_obj .validated_data ) return Response(ser_obj .errors )
serializers.Serializer序列总结 使用serializers.Serializer方法序列化时注意以下几点:
正序与反序字段不一致
字段中的参数 required=False 指明反序时(前端提交数据过来)不做数据校验
read_only=True 参数指明该字段只做序列化,反序列化时不走该字段;那么反序列化时就必须重新指定字段名
write_only 参数指明该字段只用做反序列化
序列化器中必须实现create()与update()方法,分别对应创建与更新
前端提交的参数校验与Django中的form表单校验数据一样
seriarizers.ModelSerializer序列化 示例 4.1 depth 1 2 3 4 5 class BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = '__all__'
序列化后的数据如下:
1 2 3 4 5 6 7 8 { "id" :2 , "title" : "天龙八部" , "authors" : [ 1 , 2 ] }
外键关联的字段只显示了id,添加一个depth参数
1 2 3 4 5 class BookSerializer (serializers.ModelSerializer): class Meta : model = Book fields = '__all__' depth = 1
序列化后的数据如下:
1 2 3 4 5 6 7 8 { "id" :2 , "title" : "天龙八部" , "authors" : [ {"id" :1 , "name" : "a" }, {"id" :2 , "name" : "b" }, ] }
depth=1,意为找到一层关联的数据,即找到publisher关联表,如果publisher中有还有关联的表,不会再往下去找。
注意:
depth最多不要超过四层
depth会让所有的外键关系字段变成read_only=True
生产环境depth不常用
反序列化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = '__all__' extra_kwargs = { 'publisher' : {'write_only' : True }, 'authors' : {'write_only' : True }, } publisher_info = serializers.SerializerMethodField(read_only=True ) authors_info = serializers.SerializerMethodField(read_only=True ) def get_publisher_info (self, obj) : publisher_obj = obj.publisher print(publisher_obj) return {'id' : publisher_obj.id, 'title' : publisher_obj.title} def get_authors_info (self, obj) : authors_queryset = obj.authors.all() return [{'id' : author.id, 'name' : author.name} for author in authors_queryset]
外键关联的字段正序使用的是publisher_info与authors_info,那么反序还是使用的publisher与authors字段。字段的校验与上面的一样。
自定义序列字段的两种写法 这里所说的自定义序列字段可以另一张表中的字段,也可以自定要序列化的字段,也可以是外键关联的字段。 这种字段可以使用serializers.SerializerMethodField方法写在序列化器中 也可以在模型类中定义方法
1:写在序列化器中
1 2 3 4 5 6 7 8 9 class BookSerializer (serializers .ModelSerializer ): publisher = serializers.SerializerMethodField() def get_publisher (self , obj) : return obj.publisher.name class Meta : model = Book fields = ['name' , 'price' , 'publisher' ]
注意:使用serializers.SerializerMethodField方法时,在fields列表中必须写上该字段,如上面代码中的publisher。如果fields列表中没有指定publiser字段,会报如下异常:
1 The field 'publisher' was declared on serializer BookSerializer, but has not been included in the 'fields' option.
2:写在模型类中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Book(BaseModel) : name = models.CharField(max_length =64) price = models.DecimalField(max_digits =5, decimal_places =2) img = models.ImageField(upload_to ='img ', default ='img / default .jpg ') publisher = models.ForeignKey(to ='Publisher', on_delete =models .CASCADE) authors = models.ManyToManyField(to ='Author') class Meta: db_table = 'gin_book' def __str__(self ) : return self.name @property def publisher_name(self ) : # 定义成一个方法属性,方法名与属性名不能重复 return self.publisher.name
序列化类中的fields列表,可以指定publisher_name;不指定也不报异常
1 2 3 4 5 class BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = ['name' , 'price' , 'publisher_name' ]
使用该方法时反序列化如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = ['name' , 'price' , 'publisher_name' , 'img' , 'author_list' , 'publisher' , 'authors' ] extra_kwargs = { 'publisher' : {'write_only' : True }, 'authors' : {'write_only' : True }, 'name' : { 'required' : True , 'min_length' : 3 , 'error_messages' : { 'required' : '必填项' , 'min_length' : '至少3个字符' } } }
视图类:
1 2 3 4 5 6 7 8 9 10 class BooksView(APIView) : def get(self, request): queryset = Book . objects.filter(is_delete = False) serializer = BookSerializer(queryset , many = True) return Response(serializer .data ) def post(self, request): ser_obj = BookSerializer(data = request .data ) ser_obj.is_valid(raise_exception =True) # 当校验失败会马上终止当前视图方法,抛出异常给前台 book_obj = ser_obj.save() return Response({'status ': 0, 'message ': 'OK', 'result ': BookSerializer(book_obj ) .data})
自定义联表深度(嵌套) 模型类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Book(BaseModel): name = models.CharField(max_length =64) price = models.DecimalField(max_digits =5, decimal_places =2) img = models.ImageField(upload_to ='img' , default ='img/default.jpg' ) publisher = models.ForeignKey(to ='Publisher' , on_delete =models.CASCADE) authors = models.ManyToManyField(to ='Author' ) class Meta: db_table = 'gin_book' def __str__(self): return self.name class Publisher(BaseModel): name = models.CharField(max_length =64) address = models.CharField(max_length =64) class Meta: db_table = 'gin_publisher' def __str__(self): return self.name
需求:Book表序列化时,也要序列化Pulisher表中指定的字段
1 2 3 4 5 6 7 8 9 10 11 class PublisherSerializer (serializers.ModelSerializer) : class Meta : model = Publisher fields = ['name' , 'address' ]class BookSerializer (serializers.ModelSerializer) : publisher = PublisherSerializer() class Meta : model = Book fields = ['name' , 'price' , 'publisher' ]
单增群增 序列化器如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = ['name' , 'price' , 'publisher_name' , 'img' , 'author_list' , 'publisher' , 'authors' ] extra_kwargs = { 'publisher' : {'write_only' : True }, 'authors' : {'write_only' : True }, 'name' : { 'required' : True , 'min_length' : 3 , 'error_messages' : { 'required' : '必填项' , 'min_length' : '至少3个字符' } } } def validate_name (self, value) : if Book.objects.filter(name = value).exists(): raise serializers.ValidationError('书籍名已存在' ) return value def validate (self, attrs) : return attrs
单增群增,序列化器不用改。但需要判断前台数据为字典时为单增,为列表时是群增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class BooksView(APIView) : def get(self, request): queryset = Book . objects.filter(is_delete=False) serializer = BookSerializer(queryset , many =True) return Response(serializer .data ) def post(self, request): if isinstance(request.data, dict): many = False elif isinstance(request.data, list ): many = True else : return Response({'status ': 1, 'message ': 'invalid data '}) ser_obj = BookSerializer(data =request .data , many =many ) ser_obj.is_valid(raise_exception =True) # 当校验失败会马上终止当前视图方法,抛出异常给前台 results = ser_obj.save() return Response({'status ': 0, 'message ': 'OK', 'result ': BookSerializer(results , many =many ) .data})
前台传递数据格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [ { "name" : "better man" , "price" : 96.66 , "authors" : [1 ,3 ], "publisher" : 1 }, { "name" : "tomorrow will be better" , "price" : 100 , "authors" : [2 ], "publisher" : 2 } ]
单删群删 1 2 3 4 5 6 7 8 9 def delete (self, request, *args, **kwargs) : pk = kwargs.get('pk' ) if pk: pks = [pk] else : pks = request.data.get('pks' ) if Book.objects.filter(pk__in = pks, is_delete = False ).update(is_delete = True ): return Response({'status' : 0 , 'message' : 'success' }) return Response({'status' : 0 , 'message' : 'failed' })
修改数据 1:单整体修改 即修改数据时所传递的数据字段不能少
1 2 3 4 5 6 7 8 def put(self, request, pk): book_obj = Book.objects.filter(pk = pk).first() serializer = BookSerializer(instance = book_obj, data = request.data ) # 有 instance = book_obj 参数,当serializer.save()时会走update方法;如果没有instance = book_obj 参数会走create方法 # data = request.data 必须要有,因为下面的is_valid方法校验的就是data 中的数据 serializer.is_valid(raise_exception=True ) book = serializer.save() return Response(BookSerializer(book).data, status = status.HTTP_201_CREATED)
前端传递数据如下:
1 2 3 4 5 6 { "name" : "take me to you heart" , "price" : 68.3 , "publisher" : 1 , "authors" : [1 ] }
2:单局部修改 即前端只传递部分字段,传递过来的字段做数据校验,校验通过后更新;没传递过来的字段不做数据校验
1 2 3 4 5 6 7 def patch(self, request, pk): book_obj = Book.objects.filter(pk =pk).first() # 方法同上,只是多了个 partial =True 参数;即传递过来的字段做校验,没有传递过来的字段不做校验 serializer = BookSerializer(instance =book_obj, data =request.data, partial =True ) serializer.is_valid(raise_exception =True ) book = serializer.save() return Response(BookSerializer(book).data, status =status.HTTP_201_CREATED)
批量修改数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class BooksAPIView(APIView): .. .. .. .. .. .. .. .. .. .. .. .. # 请求数据格式:[{pk:1, name: 123}, {}, {}] def patch(self, request, *a rgs, **kwargs): request_data = request.data pk = kwargs.get ('pk' ) if pk and isinstance(request_data, dict): # 单改 pks = [pk] request_data = [request.data] elif not pk and isinstance(request_data, list): # 群改 pks = [] for dic in request_data: pk = dic.pop('pk' , None) pks.append(pk) if not pks: return Response({'status' : 1, 'message' : 'invalid data' , 'result' : '' }) else : return Response({'status' : 1, 'message' : 'invalid data' , 'result' : '' }) objs = [] new_request_data = [] for index, pk in enumerate(pks): try: obj = Book.objects.get (pk = pk, is_delete = False ) objs.append(obj) new_request_data.append(request_data[index]) except Exception as e: continue print (objs) print (new_request_data) book_ser = BookSerializer(instance = objs, data = new_request_data, partial = True , many = True ) book_ser.is_valid(raise_exception =True ) book_obj = book_ser.save() return Response({'status' : 1, 'message' : 'success' , 'result' : BookSerializer(book_obj, many = True ).data})
注意:批量修改数据,序列化器中必须要有ListSerializer,如果没有会抛出异常:
1 NotImplementedError: Serializers with many=True do not support multiple update by default , only multiple create. For updates it is unclear how to deal with insertions and deletions. If you need to support multiple update , use a `ListSerializer` class and override `.update()` so you can specify the behavior exactly.
序列化类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class BookListSerializer (serializers.ListSerializer) : def update (self, instance, validated_data) : print(instance) print(self.child) for index, obj in enumerate(instance): self.child.update(obj, validated_data[index]) return instanceclass BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = ['name' , 'price' , 'publisher' , 'img' , 'author_list' , 'publisher_name' , 'authors' ] extra_kwargs = { 'name' : {'required' : True , 'min_length' : 1 , 'error_messages' : {'required' : '必填项' , 'min_length' : '至少3个字符' }}, 'publisher' : {'write_only' : True }, 'authors' : {'write_only' : True } } list_serializer_class = BookListSerializer
视图类与序列化类传参 在视图类中,可以通过request得到登陆用户request.user 在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登录用户,但序列化类无法访问request对象 在视图类中实例化序列化对象时,可以通过context参数将request对象传递进去
1 book_ser = BookSerializer(context = {'request' : request}, instance = objs, data = new_request_data, partial = True , many = True )
序列化类中可以在钩子函数中接受该参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class BookSerializer (serializers.ModelSerializer) : class Meta : model = Book fields = ['name' , 'price' , 'publisher' , 'img' , 'author_list' , 'publisher_name' , 'authors' ] extra_kwargs = { 'name' : {'required' : True , 'min_length' : 1 , 'error_messages' : {'required' : '必填项' , 'min_length' : '至少3个字符' }}, 'publisher' : {'write_only' : True }, 'authors' : {'write_only' : True } } list_serializer_class = BookListSerializer def validate (self, attrs) : print(self.context.get('request' ).method) return attrs
序列化组件修改返回值to_representation、to_internal_value to_representation(self, instance):如果序列化器定义了此方法,可以改变序列化对象data的值,也就是serializer.data的值,你可以根据自己的业务场景去重新构造返回值。
1 2 3 4 5 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方法替代。
1 2 3 4 def to_internal_value(self, value): if value == None: return 0 return value
总而言之,这两个方法一个是用于重新构造validated_data并返回,一个用于重新构造serializer.data的值并返回。