drf中-APIView/请求/响应
基于APIView+Response 写接口
在views.py中
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
class BookView(APIView): # 使用cbv方法编写 继承APIView
def get(self,request):
book_list = []
books = Book.objects.all() # 拿到所有数据对象
for book in books:
book_list.append({'name':book.name,'price':book.price})
# 把数据对象转为字典 添加入一个列表
return Response(book_list)
# 使用drf内Response模块,可以直接将字典或列表序列化
分析APIView执行流程
from rest_framework.views import APIView
from rest_framework.response import Response
path('books/', views.BookView.as_view())
1.APIView的as_view方法执行:view还是原来的view,但是去掉了csrf的认证
当请求来了的时候会执行views.BookView.as_view()这个方法,因为BookView类本身没有as_view()方法,所以从父类里面查找,查到了APIView里面的as_view方法并执行了
@classmethod
# 类方法 调用父类的as_view
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs)
# super是从父类中拿as_view(**initkwargs)方法并执行
# 执行后获得了原生django中as_view中闭包函数view拿出来了
return csrf_exempt(view)
# csrf_exempt 排除所有请求的csrf认证
# 最后相当于把原生view排除了所有csrf认证
2.路由匹配成功相当于执行了 csrf_exempt(view)(request),本质还是原生的view执行了self.dispatch
self是视图类对象(bookview对象由于bookview对象中没有dispatch方法,所以就从父类中找 APIView中找到了dispatch方法
def dispatch(self, request, *args, **kwargs):
request = self.initialize_request(request, *args, **kwargs)
# 在这里把原来的request包装成了新的request
# 通过initialize_request方法看到把老的request放入了request._request中
self.request = request
# 把新的request放到了视图类的对象中
try:
# 开启异常捕获
self.initial(request, *args, **kwargs)
# 执行三大认证 认证 频率 权限
if request.method.lower() in self.http_method_names:
# 判断请求方式 是否在 self.http_method_names总
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
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
总结:1.先去除了所有请求的csrf认证
2.包装了新的request 以后视图类中的request是新的了老的request在
新的request._request里面
3.在执行视图类方法之前 先进行了3大认证
4.如果在执行3大认证或执行过程中出错 会有异常捕获
request源码分析
from rest_framework.request import Request
新的request中包含了老的 request._request就是老的
Request源码中:
def __getattr__(self, attr):
# __getattr__魔法方法:对象.属性如果该属性不存在则触发
# request.get由于新的request中没有.method方法所以会触发这个魔法方法
try:
return getattr(self._request, attr)
# 当这个方法被触发 就返回了老的request中的该方法
except AttributeError:
return self.__getattribute__(attr)
1.新的request用起来跟老的一样,是因为新的方法取不到就会默认取老的request的方法、
2.以后不管是什么数据,都从request.data中取,取出来就是字典
3.rul中携带的参数get请求携带的参数,以后从request.query_params中取
4.文件还是从request.FILES中取
class BookView(APIView):
def get(self, request):
print(self.request.data)
print(self.request.query_params)
print(self.request.FILES)
序列化组件(查询)
查询所有接口
首先创建序列化类
新建serializer.py文件:
from rest_framework import serializers
# 需要导入这个类 继承这个类
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
# 写法导致跟models类相同
price = serializers.CharField()
publish = serializers.CharField()
# 需要序列化的字段数据,这里写什么就会自动序列化什么,如果有表中不想展示的字段,
# 这里可以不写那个字段
path('books/', views.BookView.as_view()),
views.py中
创建类方法 get请求获取所有数据
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
# 获得所有数据对象 queryset列表
ser = BookSerializer(instance=books, many=True)
# 把数据对象列表交给我们创建的序列化类进行处理,instance=需要序列化的数据
# many=是否是多个数据,如果是单个数据对象不需要many属性
return Response(ser.data)
# 直接把序列化好的数据返回,序列化好的数据在ser.data中
查询单个数据接口
查询单个url需要携带参数
path('books/<int:id>', views.BookOneView.as_view()),
views.py中
class BookOneView(APIView):
def get(self, request, id):
book = Book.objects.filter(pk=id).first()
ser = BookSerializer(instance=book)
# 由于book是单个数据 不需要many属性设置 默认many=False
return Response(ser.data)
反序列化组件(新增/修改)
新增
序列化文件中serializer.py中
添加类方法
path('books/', views.BookView.as_view()),
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data):
book_obj = Book.objects.create(**validated_data)
return book_obj
# 创建create方法 并返回创建的对象
views.py视图类中添加post请求方法
class BookView(APIView):
def post(self,request):
ser = BookSerializer(data=request.data)
# 将序列化类中的数据改为request.data
# request.data就是通过校验前端传来的数据
if ser.is_valid():
# 当数据通过校验 直接保存
ser.save()
# 执行.save方法 该方法中会调用对象本身的create方法
# 我们通过序列化类产生了一个对象,然后用这个对象调用create方法
# 就是调用了序列化类里面的create方法我们刚刚写的
return Response({'code':100,'msg':'新增成功'})
else:
return Response({'code':101,'msg':ser.errors})
修改:
path('books/<int:id>', views.BookOneView.as_view()),
views.py视图类中添加put请求方法
class BookOneView(APIView):
def put(self,request,id):
book = Book.objects.filter(pk=id).first()
# 拿到数据对象
ser = BookSerializer(instance=book,data=request.data)
# 数据对象传入序列化类中,instance=需要修改的数据 data=前端传过来的数据
# 产生一个新的数据对象
if ser.is_valid():
ser.save()
# 执行.save()方法,其中通过判断是否有instance属性来判断是执行 create
# 还是执行updata 有执行了updata
return Response({'code':100,'msg':'修改成功'})
else:
return Response({'code':101,'msg':ser.errors})
在序列化类中添加 updata方法:
def update(self, instance, validated_data):
# instance=需要修改的对象 我们之前传的book
# validated_data 前端的数据
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save()
# 更改过后对象进行保存
return instance
# 记得然后在返回出去
删除资源
不涉及序列化或反序列化
class BookOneView(APIView):
def delete(self,request,id):
res = Book.objects.filter(pk=id).first()
if not res:
return Response({'code': 103, 'msg': '该文件不存在'})
res = Book.objects.filter(pk=id).first().delete()
if res:
return Response({'code':102,'msg':'删除成功'})
反序列化的校验
反序列就是 新增 和 修改 等写入数据库的动作
在序列化的类中添加钩子函数做校验
主动抛异常需要导入函数
from rest_framework.validators import ValidationError
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
# 局部钩子 name字段
def validate_name(self, name):
if len(name) < 3:
# 对name字段做限制
raise ValidationError('书名须大于3位数')
# 主动抛异常
else:
return name
# 没有问题还把这个字段反出去
# 全局钩子
def validate(self, attrs):
if attrs.get('name') == attrs.get('publish'):
raise ValidationError('书名和出版社名称不能一致')
else:
return attrs
总结:如果有问题就主动抛异常
如果没有问题还把数据返回出去
序列化跨表新增
前端数据:{"name":'moon',"phone":"110","info":{"addr":"上海",
"work":"程序猿",
}}
# 例如前端传入这样的数据 我们需要分别存入2张表 user userinfo
例如:
class UserSerializer(ModelSerializer):
name = serializer.CharField()
class Meta:
model= User
# 关联user表
fields=['name','phone','info']
# 这里相当于需要用到的字段 前端传的我们都要用 都接受过来
info = serializer.DictField(read_only=True)
# 由于user表中没有info字段 所以需要自己写来接收
def create(self, validated_data):
info = validated_data.get('info')
# 先把前端信息接收过来
info_obj = UserInfo.objects.create(addr=info.get('addr'),word=info.get('word'))
# 先创建出第二张表对象
obj = User.objects.create(name=validated_data.get('name'),
phone=validated_data.get('phone'),
userinfo_id=info_obj.pk)
# 然后在创建user表对象
return book_obj
class UserView(APIView):
def get(self,request):
return Response('123')
def post(self, request):
ser = UserSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'msg': '添加成功'})
else:
return Response({'msg': '添加失败'})
反序列化校验部分源码解析
在视图类中
ser = UserSerializer(data=request.data)
if ser.is_valid():
当执行 is_valid 方法时 反序列化开始执行
校验通过则是 true 失败 false
is_valid 方法 查找顺序 先从序列化类中查找,没有找父类 再没有找父类的父类
is_valid部分源码展示:
def is_valid(self, *, raise_exception=False):
# raise_exception=False如果设置为true那出错就会直接抛异常
# 然后我们可以利用异常捕获
if not hasattr(self, '_validated_data'):
# 判断对象中是否有属性_validated_data
try:
# self序列化类的对象,属性中没有_validated_data,一定会走这句【核心】
self._validated_data = self.run_validation(self.initial_data)
# 给对象中添加属性_validated_data
# 执行了run_validation方法 优先从对象中找,没有再找父类
except ValidationError as exc:
self._validated_data = {}
self._errors = exc.detail
else:
self._errors = {}
if self._errors and raise_exception:
raise ValidationError(self.errors)
return not bool(self._errors)
# 最后返回的就是true 和 false 如果有错误那就是false
-最终从Serializer类中找到了run_validation,而不是Field中的run_validation
def run_validation(self, data=empty):
(is_empty_value, data) = self.validate_empty_values(data)
# # 字段自己的,validates方法
if is_empty_value:
return data
value = self.to_internal_value(data)
# 这里执行了局部钩子
try:
# 这里在执行局部钩子,你如果没有那就在正常走
value = self.validate(value)
# 这里在执行全局钩子,如果全局钩子中抛异常 就会被正常捕获到
# 全局钩子没有问题 那还是正常的流程 return出值
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
.to_internal_value(data)源码:
def to_internal_value(self, data):
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
# fields写在序列化类中一个个字段类的对象
for field in fields:
# self BookSerializer的对象,反射validate_name
validate_method = getattr(self, 'validate_' + field.field_name, None)
# 很精妙 通过反射来执行你在self对象中写的局部钩子
try:
# 在执行BookSerializer类中的validate_name方法,传入了要校验的数据
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
什么是断言
# 源码中或高端代码中 大量使用try和断言
断言:assert
就是判断值是否对,对就正常进行 不对就直接抛异常
name = input('请输入用户名')
assert name == 'moon' # 断言name是moon 如果不是就会直接报错 抛异常
print('正常')
# 如果断言正确会正常执行
断言跟判断差不多,跟if差不多,这里如果不对就抛异常
drf之请求
Request源码-控制前端返回的编码格式
例如: 需求 该接口只能接受json格式的数据 不支持前端传入其他数据格式
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
# 导入3种数据格式
parser_classes
# 解析前端闯入数据的类型
方式:局部控制
class BookView(APIView):
parser_classes = [JSONParser,]
# 这样就实现了该接口只支持json格式
# 通过该变量来控制可以接受的数据格式
需求:所有接口默认只支持json格式,个别接口需要支持其他格式
方法:全局控制
在django配置文件中settings 更新默认配置
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser',
],
}
# 这样如果不使用局部控制,默认按照配置文件中设置的生效
# 如果全局配置和局部配置都有的话 生效局部配置的
def之响应
Response能够响应的编码格式
# drf 是djagno的一个app,所以要注册
# drf的响应,如果使用浏览器和postman访问同一个接口,返回格式是不一样的
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
JSONRenderer返回json格式
BrowsableAPIRenderer返回的http网页格式
# 这样可以控制响应类
# class BookView(APIView):
renderer_classes = [JSONRenderer,]
# 这样配置会无论浏览器还是postman 都是返回json格式
# 这个设置一般是不用我们设置的
Response的源码和属性
-from rest_framework.response import Response
视图类的方法最后返回的是retrun Response()
其实就是返回了一个Response实例化的对象,所以其实就是走了Response的
__init__方法 init中可以传什么参数
# Response init可以传的参数
def __init__(self,
data=None,
status=None,
template_name=None,
headers=None,
exception=False,
content_type=None)
data:就是我们返回的ser.data 可以是字典或者列表 或者字符串 直接序列化后给到前端
status:是http响应状态码 默认响应成功显示200 你也可以改但没必要
#eg:status=201
-Response('dddd',status=status.HTTP_200_OK)
# 如果有人这样写也可以理解一样的
template_name=None,可以修改响应模板的样式 了解即可
headers=None
-Response('dddd',headers={'aaa':'111'})
设置响应头 可以往响应头里面加东西 这样直接就在响应头里面加了东西
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了