from django.db import models from django.conf import settings class User(models.Model): SEX_CHOICES = ((0, '男'), (1, '女')) name = models.CharField(max_length=64, verbose_name='姓名') password = models.CharField(max_length=64) age = models.IntegerField() height = models.DecimalField(max_digits=5, decimal_places=2, default=0) sex = models.IntegerField(choices=SEX_CHOICES, default=0) # sex = models.CharField(choices=[('0', '男'), ('1', '女')]) icon = models.ImageField(upload_to='icon', default='icon/default.png') # 自定义序列化给前台的字段 # 优点:1)可以格式化数据库原有字段的数据 2)可以对外隐藏数据库字段名 3)可以直接连表操作 @property # 制造插头 def gender(self): return self.get_sex_display() @property def img(self): return settings.BASE_URL + settings.MEDIA_URL + self.icon.name def __str__(self): return self.name
from rest_framework import serializers from . import models class UserModelSerializer(serializers.ModelSerializer):
#配置类 class Meta: # 该序列化类是辅助于那个Model类的 model = models.User # 设置参与序列化与反序列化字段 # 插拔式:可以任意选择字段返回给前台(插头都是在Model类中制造) # fields = ['name', 'age', 'height', 'sex', 'icon] fields = ['name', 'age', 'height', 'gender', 'img']
from rest_framework.views import APIView from rest_framework.response import Response from . import models, serializers class UserAPIView(APIView): def get(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: # 单查 # 1)数据库交互拿到资源obj或资源objs # 2)数据序列化成可以返回给前台的json数据 # 3)将json数据返回给前台 obj = models.User.objects.get(pk=pk) serializer = serializers.UserModelSerializer(obj, many=False) return Response(serializer.data) else: # 群查 # 1)数据库交互拿到资源obj或资源objs # 2)数据序列化成可以返回给前台的json数据 # 3)将json数据返回给前台 queryset = models.User.objects.all() # many操作的数据是否是多个 serializer = serializers.UserModelSerializer(queryset, many=True) return Response(serializer.data) def post(self, request, *args, **kwargs): # 单增 # 1)从请求request中获得前台提交的数据 # 2)将数据转换成Model对象,并完成数据库入库操作 # 3)将入库成功的对象列化成可以返回给前台的json数据(请求与响应数据不对等:请求需要提交密码,响应一定不展示密码) # 4)将json数据返回给前台 return Response()
内部类
配置类
# 概念:将类定义在一个类的内部,被定义的类就是内部类 # 特点:内部类及内部类的所以名称空间,可以直接被外部类访问的 # 应用:通过内部类的名称空间,给外部类额外拓展一些特殊的属性(配置),典型的Meta内部类 - 配置类 class Book(model.Model): class Meta: db_model = "owen_book" # 配置自定义表名 class BookSerializer(serializers.ModelSerializer): class Meta: model = "Book" # 配置序列化类绑定的Model表
class BaseSerializer(Field) def __init__(self, instance=None, data=empty, **kwargs): pass instance:是接收对象的 - 对象类型数据赋值给instance data:是接收数据的 - 请求来的数据赋值给data kwargs:内部有三个属性:many、partial、context many:操作的对象或数据,是单个的还是多个的 partial:在修改需求时使用,可以将所有校验字段required校验规则设置为False context:用于视图类和序列化类直接传参使用
常见使用
# 单查接口 UserModelSerializer(instance=user_obj) # 群查接口 UserModelSerializer(instance=user_query, many=True) # 增接口 UserModelSerializer(data=request.data) # 整体改接口 UserModelSerializer(instance=user_obj, data=request.data) # 局部改接口 UserModelSerializer(instance=user_obj, data=request.data, partial=True) # 删接口,用不到序列化类
反序列化
models.py
class User(models.Model): SEX_CHOICES = ((0,'男'),(1,'女')) name = models.CharField(max_length=64,verbose_name='姓名') password = models.CharField(max_length=64) age = models.IntegerField() height = models.DecimalField(max_digits=5,decimal_places=2,default=0) sex = models.IntegerField(choices=SEX_CHOICES,default=0) # sex = models.CharField(choices=[('0','男'),('1','女')]) icon = models.ImageField(upload_to='icon',default='icon/default.jpg') @property # 制造插头 def gender(self): return self.get_sex_display() @property def img(self): return settings.BASE_URL + settings.MEDIA_URL + self.icon.name def __str__(self): return self.name
class UserAPIView(APIView): def post(self, request, *args, **kwargs): # 单增 # 1)将前台请求的数据交给序列化类处理 # 2)序列化类执行校验方法,对前台提交的所有数据进行数据校验:校验失败就是异常返回,成功才能继续 # 3)序列化组件完成数据入库操作,得到入库对象 # 4)响应结果给前台 serializer = serializers.UserModelSerializer(data=request.data) if serializer.is_valid(): # 校验成功 => 入库 => 正常响应 obj = serializer.save() return Response({ 'status': 0, 'msg': 'ok', 'result': '新增的那个对象' }, status=status.HTTP_201_CREATED) else: # 校验失败 => 异常响应 return Response({ 'status': 1, 'msg': serializer.errors, }, status=status.HTTP_400_BAD_REQUEST)
from rest_framework import serializers from . import models # 自定义serializers class UserModelSerializer(serializers.ModelSerializer): # 自定义反序列化字段(所有校验规则自己定义,也可以覆盖model已有的字段) # 覆盖model有的字段,不明确write_only会参与序列化过程 password = serializers.CharField(min_length=3,max_length=8,write_only=True) #覆盖 # 自定义字段,不明确write_only序列化会报错,序列化会从model中强行反射自定义字段,但是model没有对于字段 re_password = serializers.CharField(min_length=3,max_length=8,write_only=True) #自定义 class Meta: # 声明该序列化类是辅助于哪个Model类的 model = models.User # 设置参与序列化与反序列化的字段 # 插拔式,可以任意选择字段返回给前台(插头都是在models类中制造) # 反序列化第一波分析: # 1. name和age,在fields中标明了,且没有默认值,也不能为空,入库时必须提供,所以校验时必须提供 # 2. height,在fields中标明了,但是有默认值,所以前台不提供,也能在入库时采用默认值(可以为空的字段同理) # 3. password,没有在field中标明,所以校验规则无法检测password情况,但是即使数据校验通过了 # 也不能玩完成入库,原因是password是入库的必备条件 # 4. gender和img是自定义插拔@property字段,默认就不会进行校验 # 第二波分析: # 1)如何区分 序列化反序列化字段 | 只序列化字段(后台到前台) | 只反序列化字段(前台到后台) # 不做write_only和read_only任何限制 => 序列化反序列化字段 # 只做read_only限制 => 只序列化字段(后台到前台) # 只做write_only限制 => 只反序列化字段(前台到后台) # 2)对前台到后台的数据,制定基础的校验规则(了解) # CharField(max_length, min_length, errors_kwargs) # DecimalField(min_value, max_value, max_digits, decimal_places,error_messages) # 3)如果一个字段有默认值或是可以为空,比如height,如何做限制 # 虽然有默认值或是可以为空,能不能强制限制必须提供,可以通过required是True来限制 # 前台提交了该字段,我就校验,没提交我就不校验:1)required是False 2)有校验规则 fields = ['name','age','height','password','gender','img','re_password'] # 第三波分析 # 1)制定的简易校验规则(没有制定)后,可以再通过字段的 局部钩子 对该字段进行复杂校验 # 2)每个字段进行逐一复杂校验后,还可以进行集体的 全局钩子 校验 # 涉及对自定义反序列化字段的校验:re_password(要参与校验,但是不会入库) # 校验规则 extra_kwargs = { 'name':{ # 'write_only':True # 'read_only':True }, 'password':{ 'write_only':True, 'min_length':3, 'max_length':8, 'error_messages':{ 'min_length':'太短', 'max_length':'太长' }, }, 'height':{ # 'required':True, 'min_value':0, } }
总结:
""" 标注:序列化 => 后台到前台(读) | 反序列化 => 前台到后台(写) 1)不管是序列化还是反序列化字段,都必须在fields中进行声明,没有声明的不会参与任何过程(数据都会被丢弃) 2)用 read_only 表示只读,用 write_only 表示只写,不标注两者,代表可读可写 3) i)自定义只读字段,在model类中用@property声明,默认就是read_only @property def gender(self): return self.get_sex_display() ii)自定义只写字段,在serializer类中声明,必须手动明确write_only re_password = serializers.CharField(write_only=True) 特殊)在serializer类中声明,没有明确write_only,是对model原有字段的覆盖,且可读可写 password = serializers.CharField() 4)用 extra_kwargs 来为 写字段 制定基础校验规则(了解) 5)每一个 写字段 都可以用局部钩子 validate_字段(self, value) 方法来自定义校验规则,成功返回value,失败抛出 exceptions.ValidationError('异常信息') 异常 6)需要联合校验的 写字段们,用 validate(self, attrs) 方法来自定义校验规则,,成功返回attrs,失败抛出 exceptions.ValidationError({'异常字段': '异常信息'}) 异常 7)extra_kwargs中一些重要的限制条件 i)'required':代表是否必须参与写操作,有默认值或可以为空的字段,该值为False;反之该值为True;也可以手动修改值 """ """ 开发流程: 1)在model类中自定义 读字段,在serializer类中自定义 写字段 2)将model自带字段和所有自定义字段书写在fields中,用write_only和read_only区别model自带字段 3)可以写基础校验规则,也可以省略 4)制定局部及全局钩子 """
全局和局部钩子
局部、全局钩子,是和Meta同缩进的,属于序列化类的
局部钩子:validate_要校验的字段(self.字段的值)
全局钩子:validate(self,所有字段值的字典)
校验规则:成功返回传来的数据,失败抛出异常
局部钩子
class UserModelserializer(serializers.Serializer): ....... def validate_字段名称(self,value): if 情况不满足: raise serializers.ValidationError('异常信息') #抛出异常 else: return value
全局钩子
class UserModelserializer(serializers.Serializer): # print(attrs) password = attrs.get('password') # 只是拿出来校验 re_password = attrs.pop('re_password') # 必须取出校验,因为不能入库 if password != re_password: raise exceptions.ValidationError({'re_password': '两次密码不一致'}) else: return attrs
补充知识点:
断言