Title

内部类,Response,序列化与反序列化基础,以及基础校验规则和自定义校验规则

序列化与反序列化

序列化 => 后台数据到前台,反序列化 => 前台数据写到后台

一.内部类:

  • 概念:将类定义在一个类的内部,被定义的类就是内部类

  • 特点:内部类及内部类的所以名称空间,可以直接被外部类访问的

  • 应用:通过内部类的名称空间,给外部类额外拓展一些特殊的属性(配置),典型的Meta内部类 - 配置类

class Book(model.Model):
   class Meta:
       db_model = "owen_book"  # 配置自定义表名 通过Book.Meta.db_model就能拿到owen_book
       
class BookSerializer(serializers.ModelSerializer):
   class Meta:
       model = "Book"  # 配置序列化类绑定的Model表

二.DRF响应类:Response

  • 补充:建立超级用户表里面的一些字段代表的含义

is_supper:是否有超级用户权限,0可以登陆但没有权限,1可以登陆没有权限

is_active:表示是否活跃,0表示已被删除,1表示正则使用

is_staff:是否是后台用户,0表示非后台,则不能登陆

然后通过admin创建两个用户信息,再通过postman发送get请求到http://127.0.0.1:8000/api/users,可以接收到响应值如下

当我们发送一个不存在的请求时http://127.0.0.1:8000/api/users/4,响应会报错,这是因为没有对此种情况的请求设置响应

若我们对后台的响应进行修改,添加一个try,进行异常捕获,但是postman显示网络状态码依然是200

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:
# 添加一个try          
           try:
      # 1)数据库交互拿到资源obj或资源obj        
               obj = models.User.objects.get(pk=pk)
               # 2)序列化:将obj放到序列化组件中实例化成序列化对象
               serializer = serializers.UserModelSerializer(obj, many=False) # many表示拿到的数据多少,False表示一个
               # 3)将json数据返回给前台
               return Response({
                   'status': 0,
                   'msg': 'ok',
                   'result': serializer.data
              })
           except:
               return Response({'status': 0,
                                'msg': 'pk error',

                                })

       else:  
           queryset = models.User.objects.all()
           serializer = serializers.UserModelSerializer(queryset, many=True)# many表示拿到的数据多少,True表示多个
           return Response(serializer.data)

这时候我们去看response的源码,看看里面有哪些参数

"""
def __init__(self, data=None, status=None, template_name=None, headers=None, exception=False, content_type=None):
  pass
   
data:响应的数据 - 空、字符串、数字、列表、字段、布尔
status:网络状态码
template_name:drf说自己也可以支持前后台不分离返回页面,但是不能和data共存(不会涉及)
headers:响应头(不用刻意去管)
exception:是否是异常响应(如果是异常响应,可以赋值True,没什么用)
content_type:响应的结果类型(如果是响应data,默认就是application/json,所有不用管)
"""

之后对响应进行修改:添加网络状态码status和数据data,这样就ok了

  try:
     obj = models.User.objects.get(pk=pk)
     serializer = serializers.UserModelSerializer(obj, many=False)
     return Response(
        {'status': 0,
          'msg': 'ok',
          'result': serializer.data})

  except:
     return Response(
         # 将数据给data
         data={'status': 1,  #数据状态码 ,可以自定义
               'msg': 'pk error'},
         # 添加网络状态码
                status=400,
                exception=True)

网络状态码也可以导入,通过下面导入status会有很多封装好的状态码,导入即可用

form rest_framwork import status

。。。
Response(
   data={
       'status': 1,
       'msg': '客户端错误提示',
  },
status=status.HTTP_400_BAD_REQUEST,# 导入比直接写400更清晰
exception=True
)

三.序列化基类控制的初始化参数

上面我们提到查询数据,那往数据库添加数据该怎么设置响应呢

 def post(self, request, *args, **kwargs):
           # 单增

           
           # 4)将json数据返回给前台
           # 1)从请求request中获得前台提交的数据
           request_data = request.data
           # 2)反序列化, 将数据转换成Model对象,并完成数据库入库操作(这一步就完成序列化组件)
           # 3)将入库成功的对象序列化成可以返回给前台的json数据(请求与响应数据不对等:请求需要提交密码,响应一定不展示密码)
           serializer = serializers.UserModelSerializer(request_data)
           # 做校验
           serializer.is_valid()
           # 3)将入库成功的对象列化成可以返回给前台的json数据(请求与响应数据不对等:请求需要提交密码,响应一定不展示密码)
             # 4)将json数据返回给前台
           return Response(
              {'status':0,
                'msg': 'ok',
                'result': '新增对象'},
               status=status.HTTP_200_OK
          )
       
       
  #这样写会发现报错

报错信息:

上面反序列化时用到的UserModelSerializer方法点进去发现继承的ModelSerializer,而ModelSerializer又继承的Serializer,Serializer又继承的BaseSerializer,在BaseSerializer里面有一个init方法
"""
def __init__(self, instance=None, data=empty, **kwargs):
  pass

instance:是要被赋值对象的 - 对象类型数据赋值给instance
data:是要被赋值数据的 - 请求来的数据赋值给data
# instance和data同时被赋值,执行修改操作
kwargs:内部有三个属性:many、partial、context
  many:操作的对象或数据,是单个的还是多个的
  partial:在修改需求时使用,可以将所有校验字段required校验规则设置为False
  context:用于视图类和序列化类直接传参使用
"""

所以我们知道对象是赋值给instance的,而数据是赋值给data的,这样前台就能正常接收信息了

serializer = serializers.UserModelSerializer(data = request_data)        
补充:断言
例如:
'''
try:信息
except:信息
else:信息
当一次异常都没有的时候执行else的信息
'''

或者assert 条件,断言失败的自定义信息
   
即:断言条件通过,代码继续进行,断言失败则直接抛出异常(自定义异常信息)

反序列化

views.py

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)

关于字段的序列化和反序列化

models.py

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)可以直接连表操作
 
   # 默认只读(read_only),这样可以设置sex是前台到后台只写,gender后台到前台只读,进行分离
   @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

serializer.py

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', 'password', 'age', 'height', 'gender', 'img']

标注:序列化 => 后台到前台(读) | 反序列化 => 前台到后台(写) 1)不管是序列化还是反序列化字段,都必须在fields中进行声明,没有声明的不会参与任何过程(数据都会被丢弃)

 

 

serializer.py

对此文件进行分析

from rest_framework import serializers
from rest_framework import exceptions

from . import models
class UserModelSerializer(serializers.ModelSerializer):
   # 自定义反序列化字段(所有校验规则自己定义,也可以覆盖model已有的字段)
   # 覆盖model有的字段(虽然下面定义了规则,但是在这依然将其规则覆盖),不明确write_only会参与序列化过程
   password = serializers.CharField(min_length=4, max_length=8, write_only=True)
   # 自定义字段,比如密码的二次校验,不明确write_only序列化会报错,序列化会从model中强行反射自定义字段,但是model没有对应字段,即必须 write_only=True
   re_password = serializers.CharField(min_length=4, max_length=8, write_only=True)


   class Meta:
       # 该序列化类是辅助于那个Model类的
       model = models.User
       # 设置参与序列化与反序列化字段
       # 插拔式:可以选择性返回给前台字段(插头都是在Model类中制造,包括通过@property设置的)
       # fields = ['name', 'age', 'height', 'sex', 'icon]

       # 第一波分析:
       # 1)name和age,在fields中标明了,且没有默认值,也不能为空,入库时必须提供,所有校验时必须提供
       # 2)height,在fields中标明了,但是有默认值,所有前台不提供,也能在入库时采用默认值(可以为空的字段同理)
       # 3)password,没有在fields中标明了,所以校验规则无法检测password情况,但是即使数据校验提供了,也不能完成入库,原因是password是入库的必备条件
       # 4)gender和img是自定义插拔@property字段,默认就不会进行校验(不参与校验)
       # fields = ['name', 'age', 'height', 'gender', 'img']

       # 第二波分析:
       # 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来限制
       #       虽然有默认值或是可以为空,能不能强制限制必须提供,可以通过required是True来限制
       #       前台提交了该字段,我就校验,没提交我就不校验:1)required是False 2)有校验规则,可以在class Meta上面通过serializers点CharField等的方式来查询是否又校验规则
       fields = ['name', 'age', 'password', 'height', 'gender', 'img', 're_password']

       # 第三波分析
       # 1)制定的简易校验规则(没有制定)后,可以再通过字段的 局部钩子 对该字段进行复杂校验
       # 2)每个字段进行逐一复杂校验后,还可以进行集体的 全局钩子 校验
       #       涉及对自定义反序列化字段的校验:re_password(要参与校验,但是不会入库)

       # 校验规则
       extra_kwargs = {
           
           #不做write_only和read_only任何限制 => 序列化反序列化字段
           'name': {
               
               # 'write_only': True,
               # 'read_only': True
          },
           
           # 只做write_only限制 => 只反序列化字段(前台到后台)
           'password': {
               # 对前台到后台的数据,制定基础的校验规则
               'write_only': True,
               'min_length': 3,
               'max_length': 8,
               'error_messages': {  # 可以被国际化配置替代
                   'min_length': '太短',
                   'max_length': '太长'
              }
          },
           'height': {
               # 'required': True,
               'min_value': 0,
          }
      }

   # 注:局部全局钩子,是和Meta同缩减,属于序列化类的
   # 局部钩子:validate_要校验的字段(self, 要校验的字段值)
   # 全局钩子:validate(self, 所有字段值的字典)
   # 校验规则:成功返回传来的数据 | 失败抛出异常

   def validate_name(self, value):
       if 'g' in value.lower():
           raise exceptions.ValidationError("这个g不行")
       else:
           return value

   def validate(self, attrs):
       print(attrs)
       password = attrs.get('password')  # 只是拿出来校验
       re_password = attrs.pop('re_password')  # 必须取出校验,因为不能入库

       if password != re_password:
           raise exceptions.ValidationError({'re_password': '两次密码不一致'})# 自定义报错信息
       else:
           return attrs

 

 

posted @ 2020-05-13 16:10  Mr江  阅读(758)  评论(0编辑  收藏  举报