基于APIView编写5个接口
之前是基于django的原生View编写接口
现在用基于drf的APIView编写接口
# drf提供了一个APIView类,以后用drf写视图类都是继承这个类或其子类,APIView本身就是继承了django原生的View
数据准备:
1.在【models.py】中创建book表
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
2.表迁移完直接双击db.sqlite3 #省时间这里直接用sqlite.3来做数据库
makemigrations
migrate
3.录入数据:随便录两条数据即可
1)基于APIView+JsonResponse
JsonResponse:主要序列化字典,非字典的需要加参数safe=False
举一个接口例子即可:查询所有图书
# 【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入JsonResponse序列化模块
from django.http import JsonResponse
# 基于APIView+JsonResponse编写接口
class BookView(APIView):
# 查询所有图书数据
def get(self, request):
books = Book.objects.all() # 结果是queryset[数据对象,数据对象..]
# 先把queryset转成列表 再使用JsonResponse序列化返回给前端
book_list = []
for i in books:
book_list.append({'name': i.name, 'price': i.price, 'publish': i.publish})
# JsonResponse主要是序列化字典的,其他类型需加参数safe=False
return JsonResponse(book_list, safe=False)
——————————————————————————————————————
# 【urls.py】
from app01 import views
path('api/v1/books/', views.BookView.as_view()),
2)基于APIView+Response
Response:无论列表还是字典都可以序列化
举一个接口例子即可:查询所有图书
# 【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入Response模块
from rest_framework.response import Response
# 基于APIView+Response编写接口
class BookView(APIView):
# 查询所有图书数据
def get(self, request):
books = Book.objects.all() # 结果是queryset[数据对象,数据对象..]
# 先把queryset转成列表 再使用Response序列化返回给前端
book_list = []
for i in books:
book_list.append({'name': i.name, 'price': i.price, 'publish': i.publish})
# Response:无论列表还是字典都可以序列化
return Response(book_list)
——————————————————————————————————————
# 【urls.py】
from app01 import views
path('api/v1/books/', views.BookView.as_view()),
APIView的执行流程
path('api/v1/books/', views.BookView.as_view()),
1.当请求来的时候执行views.BookView.as_view(),此时的as_view()是APIView的as_view
# APIView的as_view方法:发现父类APIView(View)继承了原来的View,且以后再也没csrf认证了
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
# 调用父类的as_view,父类是django原生的View
# 把djagno原生View的as_view方法中的闭包函数view拿出来了
view = super().as_view(**initkwargs)
# csrf_exempt可不校验csrf认证 相当于在所有的方法上面加了这个装饰器(以后所有的post或get请求都不会有csrf认证)
return csrf_exempt(view)
"""
补充:装饰器的基本原理
def auth() # 装饰器
def add() # 函数
# 使用auth装饰add函数
@auth # 本质:add=auth(add)
def add()
以后再使用add,其实就是在使用 auth(add) 的返回结果
"""
2.当路由匹配成功后会执行csrf_exempt(view)(request)>>父类View中的as_view中的闭包函数view。
发现里面:return self.dispatch(request, *args, **kwargs) #此时的self是视图类的对象(BookView) 在视图类中找dispatch,没有就去父类APIView中找发现可以找到。 所以当请求来匹配成功后会执行APIView中的dispatch
class APIView(View):
def dispatch(self, request, *args, **kwargs):# request是django原生的request(老的request)
# 把老的request包装成了新的request(这个是drf提供的Request类的对象)
request = self.initialize_request(request, *args, **kwargs)
"""
进入initialize_request查看发现返回了一个新的Request(drf提供的Request类的对象),且把老的request传给了Request:
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
...
)
--------------------------------
进入Request后发现_request=老的request:
def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
...
self._request = request
"""
# 所以现在这个request就是新的,老的request就是新的request._request
# 把新的request放到了self对象【BookView的对象】中也就是把老的request包在新的request中
self.request = request
try:
# 执行了三大认证(认证,频率,权限),使用的是新的request,先不看一会再回来看
self.initial(request, *args, **kwargs)
# 判断当前请求方式并转小写 在不在 8大请求方式中
if request.method.lower() in self.http_method_names:
# 在则利用反射拿到自己对象【视图类的对象:BookView】中对应的方法
# handler = getattr(自己对象,'get','获取不到的报错信息')
# handler = BookView中的get
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# request=执行反射之后获取到的方法加括号:get()
# handler把新的request传入了,所以views.py视图类中的get(self,request)中的request也是新的
response = handler(request, *args, **kwargs)
# 在执行3大认证或视图类中方法的过程中出了异常,都能被下方捕获处理
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
# 返回self.之后反射后获取到的方法加括号:get()
return self.response
总结(背)
1.去除了所有的csrf认证
2.包装了新的request,以后在视图类中用的request就是新的request('def提供的Request类的对象'),不再是原生的request
# 原生的request:新的request._request
3.在执行视图类的方法前执行了3大认证(认证,频率,权限)
4.如果在执行3大认证或视图类中方法的过程中出了异常会被异常捕获>>也是后面要说的全局异常捕获
Request对象源码分析(了解)
此时得到了一个新的Request
# 老的是这个类:django.core.handlers.wsgi.WSGIRequest
# 新的是这个类:from rest_framework.request import Request
'新的request包含了老的request'
'老的request就是:新的request._request'
rest_framework中找from rest_framework.request import Request
直接点Request查看源码:
发现左侧有很多方法看重点的几个
1.__getattr__方法 (对象.一个名字 当名字不存在时会自动触发)
如果在视图类中执行request.method(request是新的request,它里面没有method,所以就会触发Request中的__getattr__方法)
class Request:
# 新的Request中没有method属性
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
# 所以就会触发__getattr__方法
def __getattr__(self, attr):
try:
# 从老的request中反射出要.的属性
return getattr(self._request, attr)
# 如果老的没有该属性则捕获报错
except AttributeError:
return self.__getattribute__(attr)
# 结论:由此可得新的request用起来和老的request一样
________________________________
2.data方法 (上面用@property 把方法伪装成了属性 可以用对象直接点该方法获取结果)
class Request:
@property # 把方法伪装成属性
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
# 结论:今后无论是什么请求方式、编码格式,只要是在body中的数据都可以用request.data获取(取出来是字典)
________________________________
3.query_params方法 (上面用@property 把方法伪装成了属性 可以用对象直接点该方法获取结果)
# 或许会觉得多此一举,但是英文名叫查询参数 为了符合restful规范(请求地址中带查询参数[过滤条件])
class Request:
@property # 把方法伪装成属性
def query_params(self):
return self._request.GET
# 结论:get请求携带的参数可以在这里取,也可以不遵循规范直接request.POST
________________________________
4.FILES (上面用@property 把方法伪装成了属性 可以用对象直接点该方法获取结果)
class Request:
@property
def FILES(self):
if not _hasattr(self, '_files'):
self._load_data_and_files()
return self._files
# 结论:前端提交过来的文件可以在这里取(新的旧的都是用request.FILES取)
总结(背)
1.新的request用起来和老的request一样# 当新的取不到就会用__getattr__去老的里面取
2.request.data 无论什么编码、请求方式 只要在body中的数据就用它取#取出来是字典
3.request.query_params#其实就是老的request.GET(新的request._request.GET)
4.request.FILES#获取上传的文件
二.序列化组件(重点)
1.序列化组件简介
前后端通常使用json数据格式交互,在后端当我们想把一个对象返回给前端时发现json不能序列化对象,所以就有了序列化组件。通过自定义特定结构可以将对象返回给前端,同时还能对前端传入的数据进行数据校验等功能,其实就是【序列化】【反序列化】【反序列化的过程中做数据校验】
序列化组件是drf提供的类,自己写一个类然后继承该序列化类,使用其中某些方法即可完成上面的三种操作
drf提供了两个类:Serializer
和ModelSerializer
基于APIView+Response+序列化类编写接口
2.序列化类
数据准备
1.在【models.py】中创建book表
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
2.表迁移完直接双击db.sqlite3 #省时间这里直接用sqlite.3来做数据库
makemigrations
migrate
3.录入数据:随便录两条数据即可
首先需要定义一个序列化类!
# 在app01中新建【serializer.py】
# 导入序列化类
from rest_framework import serializers # 里面还有一个ModelSerializer类
# 编写一个类继承序列化类
class BookSerializer(serializers.Serializer):
# 要序列化的字段 一般是和models下的类对应
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
1)序列化多条(查询所有书)
#【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入Response模块
from rest_framework.response import Response
# 导入刚刚编写的继承序列化类的类
from .serializer import BookSerializer
class BookView(APIView):
# 查询所有图书数据
def get(self, request):
books = Book.objects.all() # 结果是queryset[数据对象,数据对象..]
# instance 要序列化的数据books
# many=True 只要时queryset对象就要加该参数,如果是单个对象则不用
ser = BookSerializer(instance=books, many=True)
# Response:无论列表还是字典都可以序列化 data就是把指定的那些字段去完成序列化
return Response(ser.data)
————————————————————————————————————————
#【urls.py】
path('api/v1/books/', views.BookView.as_view()),
2)序列化单条(查询一本书)
#【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入Response模块
from rest_framework.response import Response
# 导入刚刚编写的继承序列化类的类
from .serializer import BookSerializer
class BookDetailView(APIView):
# 查询一本图书数据
def get(self,request,pk):
book = Book.objects.filter(pk=pk).first()
# instance 要序列化的数据books
# many=True 只要时queryset对象就要加该参数,如果是单个对象则不用
ser = BookSerializer(instance=book)
# Response:无论列表还是字典都可以序列化 data就是把指定的那些字段去完成序列化
return Response(ser.data)
————————————————————————————————————————
#【urls.py】
path('api/v1/books/<int:pk>/',views.BookDetailView.as_view())
3.反序列化类
1)反序列化新增(新增一本书)
#【serializer.py】
# 导入序列化类
from rest_framework import serializers # 里面还有一个ModelSerializer类
# 导入Book表
from .models import Book
# 编写一个类继承序列化类
class BookSerializer(serializers.Serializer):
# 要序列化的字段 一般是和models下的类对应
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 新增数据必须自己在序列化类中写一个create方法,当views视图中调用ser.save()会自动触发
def create(self, validated_data):
# validated_data 前端传过来校验后的数据{name:xx,price:xx,publish:xx}
# 保存到数据库 打散数据变成k=v,k=v型式
book = Book.objects.create(**validated_data)
# 把新增的对象返回出去※
return book
————————————————————————————————————————
#【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入Response模块
from rest_framework.response import Response
# 导入刚刚编写的继承序列化类的类
from .serializer import BookSerializer
class BookView(APIView):
# 新增一本图书数据
def post(self, request):
# 前端提交过来的数据从request.data中取出 并把数据给data参数
ser = BookSerializer(data=request.data)
# 判断校验数据是否全部符合要求,有一个不符合就是False
if ser.is_valid():
# 如果符合则保存(还需要在自己写的序列化类中添加create方法)
# 当调用ser.save会自动触发序列化类中的create方法(如果instance有值则执行update,没值则执行create)
ser.save()
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
————————————————————————————————————————
#【urls.py】
path('api/v1/books/', views.BookView.as_view()),
用了request.data就不用管前端的编码方式是什么 三种都可以传
2)反序列化修改(修改一本书)
#【serializer.py】
# 导入序列化类
from rest_framework import serializers # 里面还有一个ModelSerializer类
# 导入Book表
from .models import Book
# 编写一个类继承序列化类
class BookSerializer(serializers.Serializer):
# 要序列化的字段 一般是和models下的类对应
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 修改数据
def update(self, instance, validated_data):
# instance 要修改的对象
# validated_data 前端传过来校验后的数据{name:xx,price:xx,publish:xx}
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save() # orm中修改了单个对象的属性只要调用对象.save()就能把修改保存到数据库
# 把修改后的对象返回出去※
return instance
————————————————————————————————————————
#【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入Response模块
from rest_framework.response import Response
# 导入刚刚编写的继承序列化类的类
from .serializer import BookSerializer
class BookDetailView(APIView):
# 修改一本图书数据
def put(self, request, pk):
# 找到要修改的数据
book = Book.objects.filter(pk=pk).first()
# 反序列化保存:借助序列化类
# 前端提交过来的数据从request.data中取出 并把数据给data参数
# instance 接收要修改的对象
ser = BookSerializer(data=request.data, instance=book)
# 判断校验数据是否全部符合要求,有一个不符合就是False
if ser.is_valid():
# 如果符合则保存(还需要在自己写的序列化类中添加update方法)
# 当调用ser.save会自动触发序列化类中的保存方法(如果instance有值则执行update,没值则执行create)
ser.save()
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
————————————————————————————————————————
#【urls.py】
path('api/v1/books/<int:pk>/',views.BookDetailView.as_view())
3)删除单条(删除一本书)
和序列化没关系 直接写即可
#【views.py】
# 导入APIView
from rest_framework.views import APIView
# 导入Book表
from .models import Book
# 导入Response模块
from rest_framework.response import Response
class BookDetailView(APIView):
# 删除一本图书数据
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
反序列化的数据校验功能
# 序列化类的反序列化有个数据校验功能(类似forms组件)
-局部钩子
-全局钩子
#【serializer.py】
# 导入序列化类
from rest_framework import serializers # 里面还有一个ModelSerializer类
# 导入Book表
from .models import Book
# 导入异常类
from rest_framework.exceptions import ValidationError
# 编写一个类继承序列化类
class BookSerializer(serializers.Serializer):
# 要序列化的字段 一般是和models下的类对应
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 保存数据
def create(self, validated_data):
...
# 修改数据
def update(self, instance, validated_data):
...
# 写局部/全局 钩子
局部钩子:要求name前不能是sb开头
# 局部钩子:
def validate_name(self,name):
# 校验name是否合法 startswith()校验字符串开头是否为某个值
if name.startswith('sb'):
# 校验不通过,主动抛异常
raise ValidationError('名字不能以sb开头')
else:
return name
全局钩子:要求书名跟出版社名字不能一致
# 全局钩子
def validate(self, attrs):
# attrs 校验后的数据
if attrs.get('name') == attrs.get('publish'):
# 校验不通过,主动抛异常
raise ValidationError('书名和出版社名字不能一致')
else:
return attrs
标签:request,self,APIView,接口,import,序列化,data,drf
From: https://www.cnblogs.com/lvqingmei/p/17084477.html