解析模块

为什么要配置解析模块

1)drf给我们提供了多种解析数据包方式的解析类  form-data/urlencoded/json
2)我们可以通过配置来控制前台提交的哪些格式的数据后台在解析,哪些数据不解析
3)全局配置就是针对每一个视图类,局部配置就是针对指定的视图来,让它们可以按照配置规则选择性解析数据

源码入口

# APIView类的dispatch方法中
request = self.initialize_request(request, *args, **kwargs) # 点进去

#准备要解析的数据
parser_context=self.get_parser_context(request) #不是重点
# 解析模块,获取解析类
parsers=self.get_parsers() # 点进去 # 去类属性(局部配置) 或 配置文件(全局配置) 拿 parser_classes
return [parser() for parser in self.parser_classes]

解析模块源码:rest_framework/parsers.py

自定义全局配置:项目settings.py文件

REST_FRAMEWORK = {
# 全局解析类配置
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser', # json数据包
'rest_framework.parsers.FormParser', # urlencoding数据包
'rest_framework.parsers.MultiPartParser' # form-date数据包
],
}

自定义局部配置:应用views.py的具体视图类(设置了局部就不用设置全局)

from rest_framework.parsers import JSONParser
class Book(APIView):
# 局部解析类配置,只要json类型的数据包才能被解析
parser_classes = [JSONParser]
pass

代码示例

在局部设置只能解析json格式数据,在postman其他格式传递的数据都不显示

url拼接的参数,通过request.query_params获取参数。数据包传参通过request.data获取

from . import models
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import JSONParser #用作解析类 class Book(APIView): # 局部解析类配置,post提交数据只能解析json格式数据
parser_classes = [JSONParser] #如果[]为空,那么就相当于没有设置解析类型
def post(self, request, *args, **kwargs):
# url拼接参数:只有一种传参方式就是拼接参数
print(request.query_params)
# 数据包参数:有三种传参方式,form-data、urlencoding、json
print(request.data)
return Response('post ok')

异常模块  (走到逻辑异常都能被控制)

为什么要自定义异常模块

1)所有经过drf的APIView视图类产生的异常,都可以提供异常处理方案
2)drf默认提供了异常处理方案(rest_framework.views.exception_handler),但是处理范围有限
3)drf提供的处理方案两种,处理了返回异常现象,没处理返回None(后续就是服务器抛异常给前台)
4)自定义异常的目的就是解决drf没有处理的异常,让前台得到合理的异常信息返回,后台记录异常具体信息

源码分析

# 异常模块:APIView类的dispatch方法中
response = self.handle_exception(exc) # 点进去

#自定义异常就是提供异常处理函数exception_handler,处理的目的就是让response一定有值
#自定义:自己写exception_handler函数,在自己的配置文件配置EXCEPTION_HANDLER指向自己的处理异常函数
exception_handler = self.get_exception_handler() #点进去 #获取处理异常的方法,也可以自定义异常处理方法,在settings文件中配置

#settings中获取异常处理的方式
return self.settings.EXCEPTION_HANDLER
#异常处理的结果 # 自定义异常就是提供exception_handler异常处理函数,处理的目的就是让response一定有值
response = exception_handler(exc, context) #最后发现在views中的exception_handler就是处理异常的方法

如何使用:自定义exception_handler函数如何书写实现体

# 修改自己的配置文件setting.py
REST_FRAMEWORK = {
# 全局配置异常模块
'EXCEPTION_HANDLER': 'api.exception.exception_handler', #设置自定义异常文件路径,在api应用下创建exception文件,exception_handler函数
}

api应用下创建处理异常文件exception.py   

1)先将异常处理交给rest_framework.views的exception_handler去处理  (******)
2)判断处理的结果(返回值)response,有值代表drf已经处理了,None代表drf处理不了的异常,需要自定义去处理 (******) # 自定义异常处理文件exception,重写exception_handler函数
from rest_framework.views import exception_handler as drf_exception_handler #drf原生处理异常函数取别名 drf_exception_handler
from rest_framework.views import Response
from rest_framework import status
def exception_handler(exc, context): #自定义处理异常函数
# drf的exception_handler做基础处理
response = drf_exception_handler(exc, context)
# 为空,就是drf框架处理不了的异常
if response is None: #处理之后为空,再进行自定义的二次处理
# print(exc) #错误原因 还可以做更详细的原因,通过判断exc信息类型
# print(context) #错误信息
print('%s - %s - %s' % (context['view'], context['request'].method, exc))
return Response({
'detail': '服务器错误'
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
return response #处理之后有值,就直接返回结果

异常模块的大致流程:从dispatch中的handle_exception进入,get_exception_handler()获得处理异常方法exception_handler(),在这里也可以自定义异常方法。执行exception_handler()获取异常处理的结果。

响应模块

响应类构造器:rest_framework.response.Response

def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
:param data: 响应数据
:param status: http响应状态码
:param template_name: drf也可以渲染页面,渲染的页面模板地址(不用了解)
:param headers: 响应头
:param exception: 是否异常了
:param content_type: 响应的数据格式(一般不用处理,响应头中带了,且默认是json)
"""
pass

使用:常规实例化相应对象

# status就是解释一堆 数字 网络状态码的模块
from rest_framework import status就是解释一堆 数字 网络状态码的模块
# 一般情况下只需要返回数据,status和headers都有默认值
return Response(data={数据}, status=status.HTTP_200_OK, headers={设置的响应头})

序列化组件(下面都是讲关于Serializer)

知识点:Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)

为什么使用序列化组件:因为视图中查询到的对象和queryset类型不能直接作为数据返回给前台,所以要使用序列化组件(******)

1.定义Serializer类

  • 模型层:models.py
class User(models.Model):
SEX_CHOICES = [
[0,'男'],
[1,'女'],
] name = models.CharField(max_length=64)
pwd = models.CharField(max_length=32)
phone = models.CharField(max_length=11,null=True,default=None)
sex = models.IntegerField(choices=SEX_CHOICES,default=0)
icon = models.ImageField(upload_to='icon',default='icon/default.jpg') class Meta:
db_table='old_boy_user'
verbose_name='用户'
verbose_name_plural=verbose_name def __str__(self):
return '%s' %self.name

自定义序列化类(自定义serializer类):

from rest_framework import serializers
class User(serializers.Serializer):
id = serializers.IntergetField() #序列化括号里面不用加条件
   name = serializer.CharField()
  .
  .
  .
  • 配置层:settings.py
# 注册rest_framework
INSTALLED_APPS = [
# ...
'rest_framework',
]

2.分析源码创建Serializer对象中参数

源码分析 BaseSerializer

def __init__(self, instance=None, data=empty, **kwargs):

定义好Serialzier类后,就可以创建Serializer对象了。

Serializer的构造方法为:

Serializer(instance=None,data=empty,**kwargs)

说明:

1.用于序列化时,将模型类对象传入instance参数

2.用于反序列化时,将要被反序列化的数据传入data参数

3.除了instance和data参数外,在构造Serializer对象时,还可以通过context参数额外添加数据   通过context参数附加的数据,可以通过Serializer对象的context属性获取

serializer = AccountSerializer(account,context={'request':request})

总结注意:(******)

1.使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以
2.序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。(data,instance传参)
序列化是数据对象从数据库中查出,通过instance传入序列化器中,必须通过data属性才能将序列化后的数据传给前端,不能直接传序列化对象
反序列化是数据是通过request.data从前端获取到数据,通过data传入序列化器中进行校验,保存到数据库中
3.序列化器的字段声明类似于我们前面使用过的表单系统
4.开发restful api时,序列化器会帮我们把模型数据转换成字典。
5.drf提供的视图会帮我们把字典转换成json,或者把客户端发过来的数据转换成字典

3.序列化器的使用

序列化器的使用分两个阶段:

  1.在客户端请求时:使用序列化器可以完成对数据的反序列化(就是前段往后端传递数据,反序列化之后保存数据)

  2.在服务器响应时,使用序列化器可以完成对数据的序列化(服务器取出数据,序列化之后往前段发送展示)

序列化使用流程:

基本使用:

  1.先查询出一个用户对象

from models import user
user = User.object.get(id=2)

  2.构造序列化器对象

from user.serializers import UserSerializer
user_obj = Userserializer(user) #放入查询出的user对象

  3.获取序列化对象   通过data属性可以获取序列化后的数据

上面查出来的user_ser是一个serializer对象,需要取出具体的数据传给前端,所有要用到 user_ser.data取出具体数据
user_ser = Userserializer(user).data

  4.如果要被序列化的数据是包含多条数据的(也可以说被[ ]嵌套的,queryset类型数据,不管是多条还是单条),需要添加many=True参数

user = models.User.objects.all()
user_ser = Userserialzier(user,many=True)

  5.自定义序列化属性(重点******)  下面有代码示例具体操作

serializers.SerializerMethodField()

反序列化使用流程:

数据验证:

1.使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

2.在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False

3.验证失败,可以通过序列化对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。

4.验证通过,可以通过序列化器对象的validated_data属性获取数据

保存数据:

序列化类中必须重写create方法用于新增,重写update方法是修改

视图中使用create()和save()方法

从源码可知save()方法内部调用的是序列化类中的create方法,所以新增必须要在序列化类中重写create方法

4.代码示例序列化和反序列化在Serialzier组件中使用

序列化使用(展示给前台的数据)

  • 序列化层:api/serializers.py     (api应用下创建serializers.py文件)
1)设置需要返回给前台数据样式 那些model类有对应的字段,不需要返回的就不用设置了
2)设置方法字段,字段名可以随意,字段值由 get_字段名 提供,来完成一些需要处理在返回的数据,类似于forms组件
from rest_framework import serializers, exceptions
from django.conf import settings
from . import models class UserSerializer(serializers.Serializer): #创建一个序列化类
name = serializers.CharField()
phone = serializers.CharField()
# 序列化提供给前台的字段个数由后台决定,可以少提供,但是提供的数据库对应的字段,名字一定要与数据库字段相同
# sex = serializers.IntegerField()
# icon = serializers.ImageField() # 自定义序列化属性
# 属性名随意,值由固定的命名规范方法提供:
# get_属性名(self, 参与序列化的model对象)
# 返回值就是自定义序列化属性的值
gender = serializers.SerializerMethodField()
def get_gender(self, obj):
# choice类型的解释型值 get_字段_display() 来访问
return obj.get_sex_display()    #自定义序列化属性icon
icon = serializers.SerializerMethodField()
def get_icon(self, obj):
# settings.MEDIA_URL: 自己配置的 /media/,给后面高级序列化与视图类准备的
# obj.icon不能直接作为数据返回,因为内容虽然是字符串,但是类型是ImageFieldFile类型
return '%s%s%s' % (r'http://127.0.0.1:8000', settings.MEDIA_URL, str(obj.icon))
  • 视图层
视图层书写的三个步骤
1)从数据库中将要序列化给前台的model对象,或是多个model对象查询出来
user_obj = models.User.objects.get(pk=pk) 或者
user_obj_list = models.User.objects.all()
2)将对象交给序列化处理,产生序列化对象,如果序列化的数据是由[]嵌套,一定要设置many=True
user_ser = serializers.UserSerializer(user_obj) 或者
user_ser = serializers.UserSerializer(user_obj_list, many=True)
3)序列化 对象.data 就是可以返回给前台的序列化数据
return Response({
'status': 0,
'msg': 0,
'results': user_ser.data
})
class User(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: #单查
try:
# 用户对象不能直接作为数据返回给前台
user_obj = models.User.objects.get(pk=pk)
# 序列化一下用户对象
user_ser = serializers.UserSerializer(user_obj)
return Response({
'status': 0,
'msg': 0,
'results': user_ser.data #如果你在序列化的时候没有.data,那么在传给前端的时候必须要.data
})
except:
return Response({
'status': 2,
'msg': '用户不存在',
})
else: #群查
# 用户对象列表(queryset)不能直接作为数据返回给前台
user_obj_list = models.User.objects.all()
# 序列化一下用户对象
user_ser_data = serializers.UserSerializer(user_obj_list, many=True).data
return Response({
'status': 0,
'msg': 0,
'results': user_ser_data
})

反序列化使用  (把数据存入数据库)

  • 反序列层:api/serializers.py
1)设置必填与选填序列化字段,设置校验规则
2)为需要额外校验的字段提供局部钩子函数,如果该字段不入库,且不参与全局钩子校验,可以将值取出校验 pop
3)为有联合关系的字段们提供全局钩子函数,如果某些字段不入库,可以将值取出校验
4)必须重写create方法,完成校验通过的数据入库工作,得到新增的对象
class UserDeserializer(serializers.Serializer):
# 1) 哪些字段必须反序列化
# 2) 字段都有哪些安全校验
# 3) 哪些字段需要额外提供校验 钩子函数
# 4) 哪些字段间存在联合校验
# 注:反序列化字段都是用来入库的,不会出现自定义方法属性,会出现可以设置校验规则的自定义属性,不入数据库的
name = serializers.CharField(
max_length=64,
min_length=3,
error_messages={
'max_length': '太长',
'min_length': '太短'
}
)
pwd = serializers.CharField()
phone = serializers.CharField(required=False)
sex = serializers.IntegerField(required=False) # 自定义有校验规则的反序列化字段,例如确认密码字段re_pwd
re_pwd = serializers.CharField(required=True) # 小结:
# name,pwd,re_pwd为必填字段
# phone,sex为选填字段
# 五个字段都必须提供完成的校验规则 # 局部钩子:validate_要校验的字段名(self, 当前要校验字段的值)
# 校验规则:校验通过返回原值,校验失败,抛出异常
def validate_name(self, value):
if 'g' in value.lower(): # 名字中不能出现g
raise exceptions.ValidationError('名字非法,是个鸡贼!')
return value # 全局钩子:validate(self, 通过系统与局部钩子校验之后的所有数据)
# 校验规则:校验通过返回原值,校验失败,抛出异常
def validate(self, attrs): #attrs是字典格式
pwd = attrs.get('pwd')
re_pwd = attrs.pop('re_pwd') #因为re_pwd不需要入数据库,所以在全局钩子校验中删除掉这个字段
if pwd != re_pwd:
raise exceptions.ValidationError({'pwd&re_pwd': '两次密码不一致'})
return attrs # 要完成新增,必须自己重写 create 方法,validated_data是校验的数据
def create(self, validated_data):
# 尽量在所有校验规则完毕之后,数据可以直接入库
return models.User.objects.create(**validated_data)
  • 视图层:views.py
反序列化视图层三步:
1)book_ser = serializers.UserDeserializer(data=request_data) # 反序列化数据必须赋值data,结果就是得到一个serializer对象
2)book_ser.is_valid() # 把数据放到自定义serializer中校验,数据校验成功返回True,失败返回False
3)不通过返回 book_ser.errors 给前台,通过 book_ser.save() 得到新增的对象,再正常返回
class User(APIView):
# 只考虑单增
def post(self, request, *args, **kwargs):
# 请求数据
request_data = request.data
# 数据是否合法(增加对象需要一个字典数据)
if not isinstance(request_data, dict) or request_data == {}:
return Response({
'status': 1,
'msg': '数据有误',
})
# 数据类型合法,但数据内容不一定合法,需要校验数据,校验(参与反序列化)的数据需要赋值给data
book_ser = serializers.UserDeserializer(data=request_data) # 序列化对象调用is_valid()完成校验,校验失败的失败信息都会被存储在 序列化对象.errors
if book_ser.is_valid():
# 校验通过,完成新增
book_obj = book_ser.save()
return Response({
'status': 0,
'msg': 'ok',
'results': serializers.UserSerializer(book_obj).data
})
else:
# 校验失败
return Response({
'status': 1,
'msg': book_ser.errors,
})

DRF框架(二)——解析模块(parsers)、异常模块(exception_handler)、响应模块(Response)、三大序列化组件介绍、Serializer组件(序列化与反序列化使用)的更多相关文章

  1. drf框架的解析模块-异常处理模块-响应模块-序列化模块

    解析模块 为什么要配置解析模块 (1).drf给我们通过了多种解析数据包方式的解析类. (2).我们可以通过配置来控制前台提交的那些格式的数据台解析,那些数据不解析. (3).全局配置就是针对一个视图 ...

  2. 第二章、drf框架 - 请求模块 | 渲染模块 解析模块 | 异常模块 | 响应模块 (详细版)

    目录 drf框架 - 请求模块 | 渲染模块 解析模块 | 异常模块 | 响应模块 Postman接口工具 drf框架 注册rest_framework drf框架风格 drf请求生命周期 请求模块 ...

  3. drf框架--基础

    目录 drf框架 导入 什么是接口 restful接口规范 原生Django实现接口 drf框架 Django CBV 和drf CBV对比 响应渲染模块 请求数据解析模块 响应模块 二次封装Resp ...

  4. Django框架11 /form组件、modelForm组件

    Django框架11 /form组件.modelForm组件 目录 Django框架11 /form组件.modelForm组件 1. form组件介绍 2. form常用字段与插件 3. form所 ...

  5. 3) drf 框架生命周期 请求模块 渲染模块 解析模块 自定义异常模块 响应模块(以及二次封装)

    一.DRF框架 1.安装 pip3 install djangorestframework 2.drf框架规矩的封装风格 按功能封装,drf下按不同功能不同文件,使用不同功能导入不同文件 from r ...

  6. drf框架 - 解析模块 | 异常模块 | 响应模块

    解析模块 为什么要配置解析模块 1)drf给我们提供了多种解析数据包方式的解析类 2)我们可以通过配置,来控制前台提交的哪些格式的数据后台在解析,哪些数据不解析 3)全局配置就是针对每一个视图类,局部 ...

  7. drf解析模块,异常模块,响应模块,序列化模块

    复习 """ 1.接口:url+请求参数+响应参数 Postman发送接口请求的工具 method: GET url: https://api.map.baidu.com ...

  8. drf框架的模块分析

    请求模块 请求模块是个什么鬼 ''' 1.drf的request是在wsgi的request基础上再次封装 2.wsgi的request作为drf的request一个属性:_request 3.新的r ...

  9. DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render

    DRF框架    全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...

随机推荐

  1. js中call,apply,bind方法的用法

    call .apply.和bind 以上这三个方法都是js function函数当中自带的方法,用来改变当前函数this的指向. call()方法 语法格式: fun.call(thisArg[,ar ...

  2. luogu P3327 [SDOI2015]约数个数和 莫比乌斯反演

    题面 我的做法基于以下两个公式: \[[n=1]=\sum_{d|n}\mu(d)\] \[\sigma_0(i*j)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\] 其中\(\ ...

  3. 07-图5 Saving James Bond - Hard Version (30 分)

    This time let us consider the situation in the movie "Live and Let Die" in which James Bon ...

  4. 关于m3u8文件, ts文件解密, hls 解密. 一些记录

    使用openssl 解密 openssl aes-128-cbc -d -in 原.ts -out 解密后.ts -nosalt -iv 偏移量 -K key16进制 其中 iv 偏移量和 key 一 ...

  5. cesium地下模式(地表透明)2

    接上一篇博客,这篇直接分析火星的源码,看它到底改了些什么. 注意:在cesium1.63.1版本改变了模块化方式,由AMD改为ES6模块化.注意引入文件加载模块时做出对应修改. 1.火星代码里修改了4 ...

  6. @RequestBody的使用

    一.说明 首先@RequestBody需要接的参数是一个string化的json,这里直接使用JSON.stringify(json)这个方法来转化 其次@RequestBody,从名称上来看也就是说 ...

  7. Scrum Meeting 博客汇总

    Scrum Meeting 博客汇总 一.Scrum Meeting 1. Alpha 第一次 Scrum Meeting 第二次 Scrum Meeting 第三次 Scrum Meeting 第四 ...

  8. Java事务以及嵌套事务

    知识点:java事务属性 Propagation取值: REQUIRED(默认值):在有transaction状态下执行:如当前没有transaction,则创建新的transaction: SUPP ...

  9. 使用Rome读取RSS报错,org.xml.sax.SAXParseException: 前言中不允许有内容。

    这是我遇到过的最奇葩的错误 new URL的时候,使用静态变量就会报错org.xml.sax.SAXParseException: 前言中不允许有内容. URL url = new URL(Strin ...

  10. ubuntu16 安装 qBittorrent ( Linux下BT下载图形工具 )

    qBittorrent 是QT编写的一款BT下载软件,支持FreeBSD, Linux, macOS, OS/2, Windows: 官网:https://www.qbittorrent.org/ 在 ...