一、序列化和反序列化
- api接口开发,最核心最常见的一个过程就是序列化
【1】序列化
- 把我们能识别的数据结构(python的字典,列表,对象)转换成其他语言(程序)能识别的数据结构。
- 例如:
- 我们在django中获取到的数据默认是模型对象(queryset)
- 但是模型对象数据无法直接提供给前端或别的平台使用
- 所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。
【2】反序列化
- 把别人提供的数据转换/还原成我们需要的格式。
- 例如:
- 前端js提供过来的json数据
- 对于python而言就是字符串
- 我们需要进行反序列化换成模型类对象
- 这样我们才能把数据保存到数据库中
【3】小结
- 序列化:
- drf称为 read(读取数据)
- 序列化
- queryset --- > json
- 返给前端
- 反序列化:
- drf称为 write(写入数据)
- 反序列化
- 字符串 --- > json
- 接收前端的数据
二、DRF的安装和快速使用
DRF(Django REST Framework)是一个强大且灵活的开发工具包,用于构建基于Django的Web API。
它提供了许多内置的功能和工具,使得编写高质量的API变得更加容易和高效。
(1) 安装DRF:
使用pip包管理器,在终端中运行以下命令来安装DRF:
pip install djangorestframework
(2) 配置DRF:
在你的Django项目的settings.py文件中,确保将DRF添加到INSTALLED_APPS列表中:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework', # 注册drf
]
(3) 创建序列化器(Serializer):
序列化器是DRF中一个重要的概念,它将Python对象转换为JSON等可被传输的格式,并可以反序列化接收到的数据。
在你的应用程序中创建一个名为serializers.py的文件,并定义你的序列化器类。
一个示例:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
在这个示例中,我们使用ModelSerializer来自动创建序列化器类。
(4) 创建视图(View):
在你的应用程序中创建一个名为views.py的文件,并定义视图类。
一个示例:
from rest_framework import generics
from .serializers import MyModelSerializer
from .models import MyModel
class MyModelListView(generics.ListCreateAPIView):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
在这个示例中,我们使用ListCreateAPIView来创建一个支持列表和创建操作的通用视图。
(5) 配置URL路由:
在你的应用程序的urls.py文件中,定义DRF的URL路由。
一个示例:
from django.urls import path
from .views import MyModelListView
urlpatterns = [
path('mymodels/', MyModelListView.as_view(), name='mymodel-list'),
]
通过上述配置,当访问/mymodels/时,将会调用MyModelListView视图。
- 以上是使用DRF进行快速开发的简要步骤。
- 当然,DRF还提供了许多其他功能,如认证、权限控制、过滤器、分页等,可以根据自己的需求进一步学习和定制。
- 也可以参考官方文档以获得更详细的信息:
- Home - Django REST framework
【补充】下载兼容性问题
- 安装命令
pip install djangorestframework
- 如果你是django2
- 直接这样装,装最新drf,他们不匹配
- ---> pip会自动把django卸载,安装最新django,安装最新drf
- django3 ,这样没有任何问题
- 强制更新
pip3 install djangorestframework --upgrade
【补充】JSON可以序列化和反序列化的类型
【补充】回顾CBV执行流程
1 请求来了---> 执行 ---> views.BookView.as_view()(request) ---> 执行View类中as_view类方法中的闭包函数view
@classonlymethod def as_view(cls, **initkwargs): def view(request, *args, **kwargs): self = cls(**initkwargs) # BookView类实例化得到对象 self.dispatch(request, *args, **kwargs) # BookView类的dispatch
2 BookView类的dispatch没有 --> View的dispatch
通过反射去self【BookView的对象】中找到 跟请求方式同名的方法 get --> get方法
def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # 真正的执行视图类中跟请求方式同名的方法
3 以什么请求方式访问 ---> 就会执行视图类中 跟请求方式同名的方法
比如:http://127.0.0.1:8000/books/ get请求
执行BookView类中get方法
三、APIView执行流程分析
1 在路由中:path('books/', views.BookView.as_view()),请求来了
2 先看 as_view()---->APIView的 as_view---> as_view执行结果跟之前一样,去除了csrf认证
@classmethod
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs) # 调用父类的 as_view,view还是View的as_view
# 以后所有请求,都不会做csrf认证了
return csrf_exempt(view)
3 请求来了执行 views.BookView.as_view()(request)--->view(request)--->csrf_exempt(view)(request)---> 内部核心 ---> return self.dispatch(request)
4 self 是 APIView类的对象 ---> APIView没有dispatch ---> APIView的dispatch,核心代码如下
def dispatch(self, request, *args, **kwargs):
# 后续的request都是 initialize_request 返回结果--> 新的request--> drf的Requet类的对象
request = self.initialize_request(request, *args, **kwargs)
# 新的request放到了 self.request中---> self是BookView类的对象
# 后续视图类的方法中 可以直接 self.request取出 当次请求的request对象
self.request = request
try:
# 执行了三大认证:
'''
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
'''
self.initial(request, *args, **kwargs)
###### 通过反射,去视图类中:BookView中执行跟请求方式同名的方法 ######
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# request是新的Request类的对象了 get方法的第一个参数request也是新的
response = handler(request, *args, **kwargs)
################# 执行视图类的方法结束 #################
except Exception as exc:
# 如果在执行三大认证或视图类方法中,出了错,都会被异常捕获,统一处理
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
5 看self.initialize_request 是APIView的
def initialize_request(self, request, *args, **kwargs):
# 类实例化得到对象,传入一些参数
# Request类--> drf提供的类
from rest_framework.request import Request
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
只要继承APIView,以后方法中得request都变了,成了 rest_framework.request.Request 的对象了但是用起来,跟之前的一模一样。
原来的是:django.core.handlers.wsgi.WSGIRequest 类的对象。
【总结】
- 以后视图类方法中得request对象,变成了新的request,它rest_framework.request.Request 的对象了,但是用起来跟之前一样
- 把新的request对象,同时放到了 视图类的对象中 self.request = request 后续从视图类中可以直接通过 self.request取出来
- 在执行视图类的方法之前,执行了三大认证
- 如果三大认证或视图类的方法执行出错,会有全局异常处理
- 以后所有的接口都去除了csrf认证
【补充】回顾装饰器
装饰器作用:在不改变原函数源代码和调用方式的基础上,为他增加新功能
import time
def add(a, b):
time.sleep(1)
return a + b
# 统计add的运行时间
# start_time = time.time()
# add(3, 4)
# end_time = time.time()
# print('运行时间是:', end_time - start_time)
# 写一个通用的,以后只要统计任意函数运行时间,都使用通用的--->改变了调用方式,不行
# def outer(func):
# start_time = time.time()
# func(3, 4)
# end_time = time.time()
# print('运行时间是:', end_time - start_time)
#
# outer(add)
def outer(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print('运行时间是:', end_time - start_time)
return res
return inner
# inner=outer(add)
# 调用inner,就是执行,inner内部包了 add函数,inner叫闭包函数
# inner() # 改变了调用方式
# 继续优化
add = outer(add)
res = add(3, 4) # add现在本质调用 inner,参数传给 inner,inner要能接收a,b 并且有返回结果
print(res)
# 以后想装饰哪个函数 得按如下操作,麻烦
# def ee():
# print('ee')
#
# ee=outer(ee)
# ee()
# python提供了一个语法糖 @ ,以后只要按如下方式编写代码,就能实现上述功能
@outer # 等同于 ee=outer(ee) 该语法糖会把被装饰器的函数ee,当做参数,传入outer,并且把outer的执行结果,返回赋值给ee
def ee():
print('ee')
# 以后ee其实已经不是ee了,是inner了,执行ee本质在执行inner
#####上述是原理####
# 总结,以后写他
def outer(func):
def inner(*args, **kwargs):
# 被装饰器函数,执行之前干事
res = func(*args, **kwargs)
# 被装饰函数,执行之后干事情
return res
return inner
# 装饰某个函数
@outer
def add(a,b):
time.sleep(1)
return a+b
【补充】APIView再回顾
在执行视图类的方法之前干了一些事
- 去除csrf
- 包装新的requet
- 在视图类中加入了 self.request
- 执行三大认证
在执行视图类的方法之后干了一些事
- 处理全局异常
本质就是做了装饰器的功能
四、Request对象分析
1 分析APIVIew时,分析出,以后request都是新的request了,是drf提供的Request的对象
from rest_framework.request import Request
2 源码解析之 __init__---> 老的request在新的内部---> request._request:
先看 __init__--->类实例化得到对象时,对对象进行初始化,往对象中放数据
def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
# 传入的request是老的,django原生的request
# 放到了self._request,self 是新的request类的对象
self._request = request
self._data = Empty
self._files = Empty
# 什么时候调用的 __init__?
新的 老的
-request = self.initialize_request(request, *args, **kwargs)
老的
return Request(request)
以后用新的跟用老的一样,为什么?
- 新的 requet.method
- 新的 request.path
【补充】魔法方法:
在类内部,以 __开头 __结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法
学过:__init__: 类名() 自动触发
__str__: print(对象) 自动触发
还有哪些? 很多---> 所有类都继承object类---> 它都在object类中
今天要学的 __getattr__
-对象.属性 ,属性不存在会触发
3 回头看 新的 requet.method用的时候,如果method不存在就会触发 Request类的 __getattr__
源码解析之 __getattr__
# 逻辑肯定是:从老的request中取出,你想要的东西
def __getattr__(self, attr):
try:
# 通过反射,去老的中取,能取到就返回,取不到,执行except代码,再取不到就报错
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
4 以后新的request中多了个属性 data ---> 前端post,put提交的请求体中得数据,都会放在request.data中,无论何种编码格式,它都是字典
【总结】
1 老的request还在新的request内部 request._request
2 新的用起来跟老的一样:原因是---> 重写了魔法方法 __getattr__
对象.属性 ---> 属性不存在会触发 __getattr__的执行---> 内部通过反射找到老的request中对应的属性
request.POST
request.GET
request.method
....
3 request.data 多了这个属性,以后无论何种编码,无论何种请求方式 --> 都能取出请求体携带的数据
- post,put,get ---> 只要放在body体中 ---> 都能取出来 --> 当字典用
- 如果是urlencoded,form-data编码:OrderedDict --> 原来的request.POST 对象
- 如果是json编码:dict
4 如果form-data编码---> 取数据:request.data ---> 取文件:request.FILES中取
5 请求地址栏中参数--> 从request.GET中取 ---> 以后还可以从:request.query_params 中取
- query_params:查询参数
- 请求地址中带查询参数---》query_params