推荐响应式网站建设,企业信息公示网查询官网,赣州网站建设渠道,广告制作公司名字序列化类的使用
使用序列化类实现五个接口功能#xff0c;但是我们发现并没有做数据校验#xff0c;也没有做反序列化#xff0c;是我们自己手动去进反序列化#xff0c;是我们自己使用for来进行拼接的#xff0c;很不方便#xff0c;我们可以使用一个drf自带的名叫序列…序列化类的使用
使用序列化类实现五个接口功能但是我们发现并没有做数据校验也没有做反序列化是我们自己手动去进反序列化是我们自己使用for来进行拼接的很不方便我们可以使用一个drf自带的名叫序列化器来完成
路由
urlpatterns [path(admin/, admin.site.urls),path(publish/,views.PublishView.as_view()),
]视图函数
class PublishView(APIView):def get(self, request):publish_list Publish.objects.all()l []for i in publish_list:l.append({name: i.name, addr: i.addr, })return Response({code: 100, msg: ok, results: l})def post(self, request):publish_list Publish.objects.create(namerequest.data.get(name), addrrequest.data.get(addr))return Response({code: 100, msg: ok, results: {name: publish_list.name, price: publish_list.addr}})class PublishDetail(APIView):def get(self, request, pk):book_list Publish.objects.filter(idpk).first()l []for i in book_list:l.append({name: i.name, price: i.price, })return Response({code: 100, msg: ok, results: l})def delete(self, request, pk):book_list Publish.objects.filter(idpk).delete()return Response({code: 100, msg: ok, results: book_list})def put(self, request):publish_list Publish.objects.update(**request.data)return Response({code: 100, msg: ok, results: {name: publish_list.name, price: publish_list.addr}})
序列化类-Serializer
Django REST framework中的Serializer使用类来定义须继承自rest_framework.serializers.Serializer。 from rest_framework import serializersfrom app01.models import Publishclass PublishSerializer(serializers.ModelSerializer):nameserializers.CharField()addrserializers.CharField()class Meta:modelPublishfields[name,addr] # 或指定所有__all__建立一个名为Serializer的py文件·写一个类继承为Serializer.Serializer在类中写需要序列化的字段在视图类中使用完成序列化 使用序列化类
导入我们自己的序列化器
from app01.Serializer import PublishSerializer# 使用序列化类做序列化
class PublishView(APIView):def get(self, request):publish_list Publish.objects.all()serPublishSerializer(instancepublish_list,manyTrue)return Response({code: 100, msg: ok, results: ser.data})class PublishDetail(APIView):def get(self, request, pk):publish_list Publish.objects.filter(idpk).first()serPublishSerializer(instancepublish_list)return Response({code: 100, msg: ok, results: ser.data}))
如果序列化多个则需要加上manyTure 使用序列化类进行校验
三层校验
字段的校验局部钩子校验失败主动使用ValidationError抛出错误全局钩子
from rest_framework import serializers
from rest_framework.exceptions import ValidationErrorfrom app01.models import Publish, Booksclass PublishSerializer(serializers.ModelSerializer):# 第一层写需要序列化的字段名name serializers.CharField()addr serializers.CharField()
# 第二层 局部钩子def validate_name(self, name):if name.startswith(sb):raise ValidationError(不能以sb开头)else:return namedef validate_addr(self, addr):if sbin addr:raise ValidationError(不能以sb开头)else:return addr# 第三层全局钩子def validate(self, attrs):# attrs是校验过后的数据print(attrs)if attrs.get(name)[:3]attrs.get(addr)[:3]:raise ValidationError(名称不能重复)return attrsclass Meta:model Publishfields [name, addr] # 或指定所有__all__ 全局钩子def validate(self, attrs):
补充attrs是已经校验过后的数据如果前端传入了数据字段但是我的类中并没有这个字段那么最终校验过后的数据attrs中是没有前端传入的数据的因为那条数据已经被删除了 put和post保存到数据库
class BookSerializer(serializers.ModelSerializer):name serializers.CharField(max_length18, min_length3)price serializers.IntegerField(max_value999, min_value10)pub_date serializers.CharField(max_length32)def validate_name(self, name):l [金瓶梅, sb, 傻逼]for i in l:if i in name:raise ValidationError(不能使用敏感字)return namedef validate_price(self, price):if price 10:raise ValidationError(价格不能小于10)elif price 999:raise ValidationError(价格不能大于999)else:return pricedef validate(self, attrs):# attrs是校验过后的数据print(attrs)if attrs.get(pub_date)[:3] attrs.get(name)[:3]:raise ValidationError(名称不能重复)return attrsdef create(self, validated_data):# validated_data:校验过的数据之前传入没有校验的数据在这没有publish Publish.objects.create(**validated_data)return publish # 一定要返回publish对象后续会拿着这个对象进行序列化ser.def update(self, instance, validated_data):# 本方法# instance.name validated_data.get(name)# instance.price validated_data.get(price)# instance.pub_date validated_data.get(pub_date)# instance.save() # book的对象save的对象保存到数据库中# 高级写法for item in validated_data:# setattr(books,name,上海出版社)setattr(instance, item, validated_data[item])instance.save()return instanceclass Meta:model Booksfields [name, price, pub_date]路由
class BookView(APIView):def get(self, request):book_list Books.objects.all()ser BookSerializer(instancebook_list, manyTrue)return Response({code: 100, msg: ok, results: ser.data})def post(self, request):ser BookSerializer(datarequest.data)if ser.is_valid():ser.save() # 使用序列化类进行保存但是会报错因为没有指定到哪一个表所以需要重新写createprint(ser.data)return Response({code: 100, msg: ok, results: ser.data})else:print(ser.errors)return Response({code: 100, msg: ser.errors})def delete(self, request, pk):book_list Books.objects.filter(idpk).delete()ser BookSerializer(instancebook_list)return Response({code: 100, msg: ok, results: ser.data})def put(self, request):book_list Books.objects.update(**request.data)ser BookSerializer(instancebook_list, datarequest.data)if ser.is_valid():ser.save()print(ser.data)return Response({code: 100, msg: 修改成功, results: ser.data})else:print(ser.errors)return Response({code: 100, msg: ser.errors})instance需要序列化的数据如果是queryset对象即便是一个数也需要使用manyTureserializers.data 转为字典格式return Responseserializers.data这样就实现序列化了视图中使用新增与修改
新增
serializersBookserializersdatareuqest.data
新增后执行视图函数中的is_valid就是执行三层校验
成功后保存ser.save()
修改
ser BookSerializer(instancebook_list, datarequest.data)
is_valid
ser.save()def update(self, instance, validated_data):Book.objects.filer(pkinstanc).update(*validated_data)return instance修改第二种方式def update(self, instance, validated_data):Book.objects.filer(pkinstanc).update(**validated_data)return instance常用的字段名
小技巧如果跟表模型中对不上可以直接使用CharField。 字段类型 字段 字段构造方式 BooleanField BooleanField() NullBooleanField NullBooleanField() CharField CharField(max_lengthNone, min_lengthNone, allow_blankFalse, trim_whitespaceTrue) EmailField EmailField(max_lengthNone, min_lengthNone, allow_blankFalse) RegexField RegexField(regex, max_lengthNone, min_lengthNone, allow_blankFalse) SlugField SlugField(max_length50, min_lengthNone, allow_blankFalse) 正则字段验证正则模式 [a-zA-Z0-9*-] URLField URLField(max_length200, min_lengthNone, allow_blankFalse) UUIDField UUIDField(formathex_verbose) format: 1) hex_verbose 如5ce0e9a5-5ffa-654b-cee0-1238041fb31a 2 hex 如 5ce0e9a55ffa654bcee01238041fb31a 3int - 如: 123456789012312313134124512351145145114 4urn 如: urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a IPAddressField IPAddressField(protocolboth, unpack_ipv4False, **options) IntegerField IntegerField(max_valueNone, min_valueNone) FloatField FloatField(max_valueNone, min_valueNone) DecimalField DecimalField(max_digits, decimal_places, coerce_to_stringNone, max_valueNone, min_valueNone) max_digits: 最多位数 decimal_palces: 小数点位置 DateTimeField DateTimeField(formatapi_settings.DATETIME_FORMAT, input_formatsNone) DateField DateField(formatapi_settings.DATE_FORMAT, input_formatsNone) TimeField TimeField(formatapi_settings.TIME_FORMAT, input_formatsNone) DurationField DurationField() ChoiceField ChoiceField(choices) choices与Django的用法相同 MultipleChoiceField MultipleChoiceField(choices) FileField FileField(max_lengthNone, allow_empty_fileFalse, use_urlUPLOADED_FILES_USE_URL) ImageField ImageField(max_lengthNone, allow_empty_fileFalse, use_urlUPLOADED_FILES_USE_URL) ListField ListField(child, min_lengthNone, max_lengthNone) 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页面时显示的字段帮助提示信息
字段校验有四层
字段自己validdators校验局部钩子全局钩子 序列化高级之soruce
soruce用来修改返回字段的
1 修改字段映射字段 publish_name表中不存在
publish_name serializers.CharField(sourcename)
2 修改字段映射方法
sb_name是表模型中一个方法
name serializers.CharField(sourcesb_name)
3 修改字段跨表查询
book表中可以链表查询
publishmodels.ForeignKey(toPublish.name) 序列化高级之返回字段
定制序列化返回字段格式
在表模型中写在序列化中做映射可以使用soruce
建表
from django.db import modelsclass Book(models.Model):name models.CharField(max_length32)price models.DecimalField(max_digits5, decimal_places2)publish models.ForeignKey(toPublish, on_deletemodels.CASCADE)authors models.ManyToManyField(toAuthor)def __str__(self):return self.nameclass Author(models.Model):name models.CharField(max_length32)age models.IntegerField()author_detail models.OneToOneField(toAuthorDetail, on_deletemodels.CASCADE)def __str__(self):return self.nameclass AuthorDetail(models.Model):telephone models.BigIntegerField()birthday models.DateField()addr models.CharField(max_length64)class Publish(models.Model):name models.CharField(max_length32)city models.CharField(max_length32)email models.EmailField()def __str__(self):return self.nameclass Meta:verbose_name 出版社verbose_name_plural verbose_namemodels
class Book(models.Model):name models.CharField(max_length32)price models.DecimalField(max_digits5, decimal_places2)publish_name models.ForeignKey(toPublish, on_deletemodels.CASCADE)author models.ManyToManyField(toAuthor)def __str__(self):return self.name# def book_name(self):# return self.namesb# def publish_detail(self):# return {name: self.name, price: self.price, publish_name: self.publish_name.addr}## def author_list(self):# l[]# for i in self.author.all():# l.append({name:i.name, age:i.age})## return l# def publish_detail(self):# return {name: self.name, price: self.price, publish_name: self.publish_name.addr}### def author_list(self):# l[]# for i in self.author.all():# l.append({name:i.name, age:i.age})# return l#class Meta:verbose_name_plural图书表view
class BookView(APIView):def get(self, request):obj Book.objects.all()ser BookSerializer(instanceobj, manyTrue)[{name: 西游记,price: 66.00,publish_detail: {name:名字,city:城市}authors_list:[{name:名字,age:19}]},]return Response(ser.data) 序列化类
### 定制返回字段
class BookSerializer(serializers.Serializer):name serializers.CharField()price serializers.CharField()#### 定制返回字段---》方案一在表模型中写方法在序列化类中做映射# publish_detail serializers.CharField() # publish_detail 会映射表模型中 publish_detail方法方法返回值是 字典强行用CharField字符串接收# publish_detail serializers.DictField() # publish_detail 会映射表模型中 publish_detail方法方法返回值是 字典用DictField接收# author_list serializers.ListField()###定制返回字段---》方案二在序列化类中写 SerializerMethodField# 只要写了这个字段类SerializerMethodField必须配合一个方法get_字段名这个方法返回什么前端这个字段就显示什么publish_detail serializers.SerializerMethodField()def get_publish_detail(self, obj):# 当前序列化到的book对象return {name: obj.publish.name, city: obj.publish.city}author_list serializers.SerializerMethodField()def get_author_list(self, obj):l []for author in obj.authors.all():l.append({name: author.name, age: author.age})return lbook_name serializers.SerializerMethodField()def get_book_name(self, obj):return obj.name sb 多表关联反序列化
# 反序列化保存
# 使用同一个序列化类会出现-序列化字段和反序列化字段不一致-序列化字段namepricepublish_detailauthor_list-反序列化字段namepricepublishauthor-如果是共同的不需要额外处理-如果是不同的需要通过字段参数控制read_only 表明该字段仅用于序列化输出默认False序列化过程write_only 表明该字段仅用于反序列化输入默认False反序列化过程 serializer
class BookSerializer(serializers.Serializer):name serializers.CharField(max_length18, min_length3, requiredTrue) # 公共的price serializers.IntegerField() # 公共的publish serializers.IntegerField(write_onlyTrue)# 反序列化author serializers.ListField(write_onlyTrue)# 反序列化publish_detailserializers.SerializerMethodField(read_onlyTrue) #序列化author_list serializers.SerializerMethodField(read_onlyTrue) #序列化def validate_name(self, name):if name.startswith(sb):raise ValidationError(不能使用敏感字开头)return namedef validate_price(self, price):if price 0:raise ValidationError(不能等于0)return pricedef validate(self, attrs):# attrs是校验过后的数据if attrs.get(name)[:3] attrs.get(addr)[:3]:raise ValidationError(名称不能重复)return attrsdef get_publish_detail(self, obj):# obj就是当前序列化到的return {name: obj.publish_name.name ,addr: obj.publish_name.addr}# 返回的 name:obj.publish.name, price:obj.publish.addr就相当于给了这个publish_detaildef get_author_list(self,obj):l[]for i in obj.author.all():l.append({name:i.name, age:i.age})return ldef create(self, validated_data):authorvalidated_data.pop(author)book Book.objects.create(**validated_data)# bookBook.objects.create(namevalidated_data[name], pricevalidated_data[price],publish_namevalidated_data[publish_name])book.author.add(*author)return book ModelSerializer
ModelSerializer跟表是一一对应的r需要重写create和update之前写的Serializer.serializer跟表没有必然的联系继承ModleSerializer可以少写很多代码
### 继承ModelSerializer---少写代码
class BookSerializer(serializers.ModelSerializer):# name serializers.CharField(max_length18,min_length3) # 公共的# price serializers.CharField() # 公共的# publish serializers.IntegerField(write_onlyTrue) # 只用来做反序列化# authors serializers.ListField(write_onlyTrue) # 只用来做反序列化# 上述操作通过 Meta实现了# 扩写的字段也要在fields注册# 方式二定制字段# publish_detail serializers.SerializerMethodField(read_onlyTrue) # 只用来做序列化# author_list serializers.SerializerMethodField(read_onlyTrue) # 只用来做序列化# def get_publish_detail(self, obj):# return {name: obj.publish.name, city: obj.publish.city}# def get_author_list(self, obj):# l []# for author in obj.authors.all():# l.append({name: author.name, age: author.age})# return l# 方式二定制字段方式一# publish_detail serializers.DictField(read_onlyTrue)# author_list serializers.ListField(read_onlyTrue)class Meta:model Book # 写了这两句会把表模型中Book所有字段映射过来# fields__all__fields [name, price, publish, authors, publish_detail, author_list]extra_kwargs { # 给某个或某几个字段设置字段属性name: {max_length: 18, min_length: 3},publish: {write_only: True},authors: {write_only: True},publish_detail: {read_only: True},author_list: {read_only: True},}# 一般不需要写create和update了---》ModelSerializer帮咱们实现了# 局部钩子和全局钩子该怎么写还怎么写 反序列化的源码分析
#1 执行 ser.is_valid() 就会执行 反序列化的校验---》字段自己--》局部钩子---》全局钩子
#2 入口是ser.is_valid()---》BaseSerializer 找到了1 自己写的BookSerializer---》serializer.Serializer----BaseSerializer 2 源码如下def is_valid(self, *, raise_exceptionFalse):# self 是 ser对象---》自己写的BookSerializer的对象--》一开始没有# 一旦有了就不执行了优化is_valid被多次调用只会走一次校验if not hasattr(self, _validated_data):try:# 一旦执行过以后self中就有_validated_data# 接下来看self.run_validation(self.initial_data)self._validated_data self.run_validation(self.initial_data)except ValidationError as exc:self._validated_data {}self._errors exc.detailelse:self._errors {}if self._errors and raise_exception:raise ValidationError(self.errors)return not bool(self._errors)3 self.run_validation(self.initial_data)---》serializer.Serializer类的不要按住ctrl点击否则会进 Field 类看错了4 serializer.Serializer类的run_validationdef run_validation(self, dataempty):# data前端传入的--{name:张三,age:68}# value是---》前端传入的字段自己校验通过的字典---{name:张三,age:68}value self.to_internal_value(data) # 执行局部钩子try:self.run_validators(value) # 先不看忽略掉# self 是 BookSerializer的对象如果我们写了全局钩子走我们自己的如果没写走父类的父类的根本没做校验# value{name:张三,age:68}value self.validate(value)# 执行全局钩子except (ValidationError, DjangoValidationError) as exc:raise ValidationError(detailas_serializer_error(exc))return value5 全局钩子读完了self 是 BookSerializer的对象如果我们写了全局钩子走我们自己的如果没写走父类的父类的根本没做校验6 局部钩子value self.to_internal_value(data)--》Serializer类的# for循环着去BookSerializer的对象中反射 validate_字段名的方法如果有就执行没有就不执行def to_internal_value(self, data):for field in fields: # 序列化类中所有字段类的对象 nameCharField()# self 是BookSerializer类的对象# 去BookSerializer类中反射 validate_field字段类的对象.field_namevalidate_method getattr(self, validate_ field.field_name, None)try:# 如果能拿到说明咱么写了局部钩子if validate_method is not None:# 执行局部钩子--》传入了当前字段的value值validated_value validate_method(validated_value)except ValidationError as exc:# 如果抛异常会被捕获errors[field.field_name] exc.detailexcept DjangoValidationError as exc:errors[field.field_name] get_error_detail(exc)except SkipField:passelse:set_value(ret, field.source_attrs, validated_value)if errors:raise ValidationError(errors)return ret# #####读了局部和全局钩子的执行位置#####
# 保存修改也好都要用validated_data它是最准确的 执行is_valid就会执反序列化的校验
字段自己的校验局部钩子全局钩子
查看源码从is_valid中去查看,建议不使用ctrl点击查看去ser,is_valid的对象就是Booksserializer中查看-----没有找到去父类的serializer中去寻找没有继续去Serializer中找is_valiid没有继续去BaseSerializer中找如果直接从ctrl中点击的话可能直接跳转到不是BaseSerializer中了。源码如下 2 源码如下def is_valid(self, *, raise_exceptionFalse):#判断self中有没有_validated_dataif not hasattr(self, _validated_data):try:self._validated_data self.run_validation(self.initial_data)except ValidationError as exc:self._validated_data {}self._errors exc.detailelse:self._errors {}if self._errors and raise_exception:raise ValidationError(self.errors)return not bool(self._errors) 逐步解析
if not hasattr(self, _validated_data):----判断self中有没有_validated_data
其中的self就是ser对象ser对象其实就是自己写的BookSerializer类的对象因为从BookSerializer进入的一开始是没有_validated_data如果有就不会执行下面的hasattr如果执行过is_valid那么之后的is_valid就不会执行了多次调用也只会执行一次所以执行下面的反射hasattr
self._validated_data self.run_validation(self.initial_data)------去Serializer中去找run_validation
不要直接ctrl直接进去找不然找到了fild里就不对了 def run_validation(self, dataempty):# 局部钩子value self.to_internal_value(data)try:self.run_validators(value) value self.validate(value) # 全局钩子# 不符合直接捕获错误except (ValidationError, DjangoValidationError) as exc:raise ValidationError(detailas_serializer_error(exc))return value
在run_validation中
全局钩子 不符合直接捕获错误 value self.validate(value) 全局钩子self是BookSerializer的对象如果自己写了钩子那么就执行我们的没写走父类父类中直接返回了并没有做校验 局部钩子
value self.to_internal_value(data)-----是Serializer的
for循环去BookSerializer的对象去反射validata_字段名的方法如果有就执行
value就前端传入的数据 def to_internal_value(self, data):for field in fields:validate_method getattr(self, validate_ field.field_name, None)#去BookSerializer中反射validate_加上字段名 validate_field字段类的对象.field_nameprimitive_value field.get_value(data)try:# 如果能拿到说明写了局部钩子否则抛出异常validated_value field.run_validation(primitive_value)if validate_method is not None:validated_value validate_method(validated_value)except ValidationError as exc:#如果抛异常会被捕获到放入下面errors中errors[field.field_name] exc.detailexcept DjangoValidationError as exc:errors[field.field_name] get_error_detail(exc)except SkipField:passelse:set_value(ret, field.source_attrs, validated_value)if errors:raise ValidationError(errors)# 最终返回return ret for field in fields: 序列化类中所有字段的对象:
validate_method getattr(self, validate_ field.field_name, None)
去BookSerializer中反射validate_加上字段名 validate_field字段类的对象.field_name 断言 assert hasattr(self, initial_data), (Cannot call .is_valid() as no data keyword argument was passed when instantiating the serializer instance.)
判断self里有没有initial_data没有就报错
a 10## assert 后写条件只要不符合条件就会抛AssertionError异常后面写异常信息
assert a 11, (不等于11报错了)# 等同于---》上面只要一行代码源码中喜欢用
if not a 11:raise Exception(不等于11报错了)# 源码中使用
assert value is not None, .validate() should return the validated data