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产生的视图类有什么省事的地方呢。

  1. 去除了所有的csrf校验

  2. 包装了新的request,视图类体函数接收的request参数是新的,self中也含新的request属性,这个新的request是drf提供的Request类产生的对象。

    ps:原本的request被装在request._request中

  3. 在执行视图类的方法之前,执行了3大认证

  4. 如果在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({})
posted @ 2023-02-05 16:18  leethon  阅读(126)  评论(0编辑  收藏  举报