web常用5个接口django和drf写法及源码简析
web常用5个接口django和drf写法及源码简析
基于django原生编写
# 路由层
urlpatterns = [
path('admin/', admin.site.urls),
path('api/book/', views.BookView.as_view()),
path('api/book/<int:pk>', views.BookView.as_view()),
]
# 视图层
import json
from django.shortcuts import render
from django.http import JsonResponse
from django.views import View
from .models import Book
from django.core import serializers
# Create your views here.
class BookView(View):
def get(self, request):
book_queryset = Book.objects.all()
return JsonResponse({
'code': 100,
'book_set': serializers.serialize('json', book_queryset)
}, safe=False)
def post(self, request):
name = request.POST.get('name')
price = request.POST.get('price')
publisher = request.POST.get('publisher')
book_obj = Book.objects.create(
name=name,
price=price,
publisher=publisher,
)
return JsonResponse({
'code': 100,
'msg': '创建成功',
'book_obj': {'name': book_obj.name, 'price': book_obj.price, 'publisher': book_obj.publisher}
})
def put(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
book_dict = json.loads(request.body)
book_obj.name = book_dict.get('name')
book_obj.price = book_dict.get('price')
book_obj.publisher = book_dict.get('publisher')
book_obj.save()
return JsonResponse({
'code': 100,
'msg': '修改成功',
'book_obj': {'name': book_obj.name, 'price': book_obj.price, 'publisher': book_obj.publisher}
})
使用以上程序写接口,可以看到,十分的繁琐,而且有一些针对请求的处理也不到位,如get目前只有查全部的功能,put只能接收json格式的数据(仅对上述程序做出的问题总结)
所以利用原生django写接口很麻烦,而且实际上也有了方案可以很快的部署这五种接口。
使用drf快速部署
drf介绍
djangorestframework:drf 帮助我们快速的实现符合restful规范的接口
drf安装
安装前提醒:drf安装时,会检测django的版本,drf最新版本认为不支持2.x版本的django,所以会递归的下载django最新版本,而我们又不想改变原有的django版本环境,所以需要回退django版本。
drf代码部署接口
# 使用drf编写5个接口
# views中
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# serializer.py中
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# urls中
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', views.BookView, 'books')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
其中需要注意:
- 这是快速编写5个接口的极简代码,实际原理涉及到很多封装好的东西
- 通过上述代码所写的5个接口还没有特别多的定制功能,通过后续的学习可以进行代码改造
- serializer文件专门用于写序列化类,在drf之序列化组件中会详细介绍
视图类View、APIView、ModelViewSet源码简析
django原生视图类
视图类又称CBV,我们通过解析源码可以看出,实际上,它通过as_view()方法得到的还是一个FBV,只是通过请求的类型将其分发(dispatch),我们可以通过在视图类中写请求类型的同名函数,来应对不同类型请求。
# 1 路由中写的:path('api/v1/books/', views.BookView.as_view()),第二个参数无论是fbv还是cbv放的都是函数内存地址
-当请求来了,匹配成功会执行,views.BookView.as_view()(request)
-views.BookView.as_view()执行结果是View的类方法as_view返回的结果是内层函数view,是个函数内层地址
-本身请求来了,匹配成功,会执行view(request)
def view(request, *args, **kwargs):
return self.dispatch(request, *args, **kwargs)
-self.dispatch View类的方法
def dispatch(self, request, *args, **kwargs):
# request.method请求方式转成小写,必须在列表中才能往下走
if request.method.lower() in self.http_method_names:
# 反射,去self【视图类的对象:BookView】,去通过get字符串,反射出属性或方法
# BookView的get方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# BookView的get方法,加括号,传入request
return handler(request, *args, **kwargs)
APIView
实际上也是继承了django的原生视图类View,而APIView做了更多的工作来方便我们编写视图函数。
以下为查询接口的一段代码,利用率drf的APIView和Response
from rest_framework.views import APIView
from rest_framework.response import Response
class BookView(APIView):
def get(self, request):
book_queryset = Book.objects.all()
return Response({
'code': 100,
'book_set': serializers.serialize('json', book_queryset) # 老的序列化工具
}) # Response可以序列化列表和字典
而继承了APIView而产生的视图类和继承了原生View产生的视图类有什么省事的地方呢。
-
去除了所有的csrf校验
-
包装了新的request,视图类体函数接收的request参数是新的,self中也含新的request属性,这个新的request是drf提供的Request类产生的对象。
ps:原本的request被装在request._request中
-
在执行视图类的方法之前,执行了3大认证
-
如果在3大认证或视图函数方法执行过程中出了错,会有异常捕获----》全局异常捕获
以下是源码解析:
点击展开APIView源码解析
# APIView的as_view方法:view还是原来的view,但是以后再也没有csrf认证了
@classmethod
def as_view(cls, **initkwargs):
# 调用父类的as_view,父类是django原生的View
# 把djagno原生View的as_view方法中的闭包函数view拿出来了
view = super().as_view(**initkwargs)
# csrf_exempt 排除所有csrf的认证
# 相当于在所有的方法上面加了这个装饰器
return csrf_exempt(view)
# 路由匹配成功,执行 csrf_exempt(view)(requets)--->View的as_view中的闭包函数view---》self.dispatch---->self是视图类的对象---》BookiView---》APIView的dispatch,找到了
def dispatch(self, request, *args, **kwargs):
# request是django原生的request,老的request
# 把老的request包装成了新的request,这个是drf提供的Request类的对象
request = self.initialize_request(request, *args, **kwargs)
# 到此以后,这个request就是新的了,老的request在哪?
# request._request 这是老的
# 把新的request放到了self对象【BookView的对象】
self.request = request
try:
# 执行了三大认证【认证,频率,权限】,使用新的request,不读
self.initial(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
# 把新的request传入了,视图类的方法中get的request也是新的
response = handler(request, *args, **kwargs)
except Exception as exc:
# 在执行3大认证和视图类中方法的过程中,如果出了异常,都能捕获到---》全局异常捕获
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
Request对象源码分析
在上述APIView中其实就对request进行了self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs):
...
return Request(request, ...)
里面就调用了Request类产生新的request对象
# Request源码
-方法 __getattr__
-在视图类的方法中,执行request.method ,新的request是没有method的,就触发了新的Request的__getattr__方法的执行
def __getattr__(self, attr):
try:
# 从老的request中反射出 要取得属性
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
-request.data--->这是个方法,包装成了数据属性
-以后无论post,put。。放在body中提交的数据,都从request.data中取,取出来就是字典
-无论是那种编码格式
-request.query_params--->这是个方法,包装成了数据属性
-get请求携带的参数,以后从这里面取
-query_params:查询参数--->restful规范请求地址中带查询参数
-request.FILES--->这是个方法,包装成了数据属性
-前端提交过来的文件,从这里取
以上为几个常用的属性,总结为:
- 通过
__getattr__
方法拿到了老request中的所有数据 - 通过request._request=request将传入的老request保存下来
- 将body中提交的数据反序列成字典或querydict的格式封装在data属性中
- 将get请求提交的数据封装到query_params中
- 将提交的文件封在FILES中,比原本的FILES也多了一定的功能
将FBV的request修改为新的request
def outer(func):
def inner(request, *args, **kwargs):
request = Request(request) # 装饰器将request替换车成新的,可通过Request直接处理
return func(request, *args, **kwargs)
return inner
@outer
def test(request):
print(request.method)
print(request.GET)
print(request.query_params)
return JsonResponse({})