APIView的使用,API源码,Request源码,序列化组件

  • APIview的基本使用

    • View+JsonResponse和APIView+drf的Response编写接口

      • View+JsonResponse编写接口

      • APIView+drf的Response编写接口

  • API源码分析

  • Request类源码分析

  •  序列化组件

  •  反序列化

 


APIview的基本使用

View+JsonResponse和APIView+drf的Response编写接口

定义字段models.py

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)

View+JsonResponse编写接口

urls.py

from django.contrib import admin
from django.urls import path
from app01.views import BookView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',BookView.as_view())
]

views.py

1. 在这里我们的BookView类继承的是View这个类,使用JsonResponse序列化

2. queryset对象是不能直接序列化的 需要通过列表的方式追加成列表套字典的形式

3. JsonResponse只能序列化字典和列表

# 视图类
from django.http import JsonResponse
from django.views import View
from .models import Book
class BookView(View):
    def get(self,request):
        book_list = Book.objects.all()  # book_list是queryset对象,不能直接序列化,需要通过列表的方式一个个追加拼成列表套字典的形式
        res_list = []
        for book in book_list:
            res_list.append({'name':book.name,'price':book.price,'publish':book.publish})
        print(res_list)
        # [{'name': '西游记', 'price': '200 ', 'publish': '东方出版社'}, {'name': '三国', 'price': '400', 'publish': '西方出版社'}]
        return JsonResponse(res_list,safe=False,json_dumps_params={'ensure_ascii':False}) # JsonResponse只能序列化字典和列表

APIView+drf的Response编写接口

urls.py

from django.contrib import admin
from django.urls import path
from app01.views import BookView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',BookView.as_view())
]

views.py

 

from rest_framework.views import APIView
from rest_framework.response import Response
class BookView(APIView):
    def get(self,request):
        book_list = Book.objects.all()
        res_list = []
        for book in book_list:
            res_list.append({'name':book.name,'price':book.price,'publish':book.publish})
        return Response(res_list)

settings.py

记得在settings中注册rest_framework

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'  # 不要忘记注册rest_framework这个app 如果不注册会报错 注册后会显示drf特有的页面
]

 


API源码分析

视图类继承了APIView之后,执行顺序就会发生变化,这个变化就是drf的整个执行流程

1. 匹配路由>>>找到视图类的as_view(但是这里是APIView的as_view)

APIView中的as_view方法---主要代码(删减版)

  csrf_exempt()

  @csrf_exempt 装饰器

@classmethod
def as_view(cls, **initkwargs):

    view = super().as_view(**initkwargs) # 这里调用了APIView的父类(View)也就是视图类的爷类(View)的as_view方法

    
    return csrf_exempt(view)  # csrf_exempt解除csrf的校验

2. 请求来了以后,路由匹配成功会执行  View里面的as_view闭包函数中的view(没有了csrf的校验),但是真正执行的是view里面的self.dispatch方法(APIView中的dispatch方法) --- 因为self是视图类 所以self.dispatch还是要从视图类开始找

View中的as_view

@classonlymethod
def as_view(cls, **initkwargs):
   
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        self.setup(request, *args, **kwargs)
        if not hasattr(self, 'request'):
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
            
        return self.dispatch(request, *args, **kwargs)
    
    view.view_class = cls
    view.view_initkwargs = initkwargs

    return view

 视图类函数(BookView)的父类(APIView)的dispatch方法

1. 其中initlize_request返回了一个 drf提供的一个Request类的对象  return Request(。。。)

  所以说视图类中的参数request,经过APIView中的dispatch中的initlize_request()操作变成了drf提供的Request类的对象

def dispatch(self, request, *args, **kwargs):
     # 上面的request参数还是django原来的request
    self.args = args
    self.kwargs = kwargs
    
    # 下面的request经过initlize_request()操作变成了drf提供的Request类的对象
    request = self.initialize_request(request, *args, **kwargs)
    
    # 之后的request不再是django原来的request,是新的request也就是视图类中的request
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        # 执行了认证,频率,权限(不读)
        self.initial(request, *args, **kwargs)
        # 原来View的dispatch的东西
        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

        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

 initialize_request  返回了一个Request对象

def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

 总结:

  1. 只要继承了APIView都没有csrf的认证

  2. 以后的视图类中的request不再是原来的request对象,已经变成了drf提供的Request对象

    因为视图类中的参数request,经过APIView中的dispatch中的initlize_request()操作变成了drf提供的Request类的对象

  3. 执行视图类的方法之前都会先执行三大认证(认证,权限,频率)

  4. 在执行视图类的方法和三大认证只要报错,都会被捕获处理

 流程:

  ---路由匹配

  ---成功执行as_view()

  ---先到APIView中找到as_view(),发现它用了super()

  ---所以到View中找as_view(),在as_view()里面有一个闭包函数,所以看as_view()里面的view()

  ---在view()里面有一个self.dispatch()方法,由于self是视图类,所以要从视图类再开始找,视图类中没有到他的父类APIView中找到dispatch(),在dispatch中有一个initlize_request()将django原来的request变成了drf提供的Request对象里面还包含了三大认证和异常捕获处理

 

 


Request类源码分析

验证视图类中的request还是不是原来的request

答:NO

执行下面的代码

print(request)
print(type(request))

Django原来的request对象

<WSGIRequest: GET '/books/'>
<class 'django.core.handlers.wsgi.WSGIRequest'>

继承了APIView的request对象

<rest_framework.request.Request: GET '/books/'>
<class 'rest_framework.request.Request'>

 request不是原来的request了,还能像原来一样使用吗

答:YES,原来怎么用,现在还是怎么用  (是因为__getattr__方法,下面会说)

 print(request.GET)

 print(request.POST)

  ........

 

 Request源码分析

from rest_framework.request import Request

在Request源码中有一个__getattr__的方法:对象.属性当属性不存在的时候会自动触发该方法

def __getattr__(self, attr): # 当属性不存在的时候自动触发
    try:
            #反射:根据字符串获取属性或方法,self._request 是原来的request
        return getattr(self._request, attr)
    except AttributeError:
        return self.__getattribute__(attr)

 (由此克制我们上面用的request方法并非自己的,而是通过Request中的__getattr__中的反射去原来的request中调用的)

在新的request中有个老的request:_request

 data

@property
def data(self):
    if not _hasattr(self, '_full_data'):
        self._load_data_and_files()
    return self._full_data

1. urlencoded,form-data:提交的数据在request.POST中

2. 在request.POST中如果是json格式上传,request.POST是拿不到的,要在request.body中取到

但是request.data是万能的 

 request.data文件也可以拿   

request.FILES

request.data 拿到的是所有

 request.data  有时候是(urlencoded,form-data)QueryDict,有时候(json)是字典

 


 序列化组件

1. 序列化组件就是drf提供的一个类(),我们可以继承它,改写

2. 它的作用就是用来序列化queryset或者单个对象的 

定义一个序列化类

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
    # 定义需要序列化的字段
    # 定义的字段类型要根据你在models中创建的字段类型来
    name=serializers.CharField()
    price = serializers.CharField()  # 如果不想显示直接注释掉就好
    publish = serializers.CharField()

 使用序列化类序列化多条数据

1. instance表示要序列化的数据

2. many=True表示序列化多条(instance是qs对象,一定要传many=True)

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from serializer import BookSerializer


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ser = BookSerializer(instance=book_list, many=True)
        return Response(ser.data)

  使用序列化类序列化一条数据

1. 要给出确定的pk值(就是那一条数据)

2. many = True去掉

urls.py

from app01.views import BookDetailView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/<int:pk>/',BookDetailView.as_view())
]

views.py

class BookDetailView(APIView):
    def get(self,request,pk):
        book = Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

 


 反序列化

urls.py

from django.contrib import admin
from django.urls import path
from app01.views import BookView,BookDetailView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',BookView.as_view()),
    path('books/<int:pk>/',BookView.as_view()),
    path('book/<int:pk>/',BookDetailView.as_view())

]

新增

 NotImplementedError: `create()` must be implemented. 这句报错的意思是create要被重写才能执行

class BookView(APIView):  # APIView继承自django的View
    def post(self, request):
        # 前端传递数据,从request.data取出来
        ser = BookSerializer(data=request.data)
        if ser.is_valid():  # 表示校验前端传入的数据   没有写校验规则,现在等于没校验
            ser.save()  # 再写东西,这里会报错  调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create
            return Response(ser.data)
        else:
            return Response(ser.errors)

 看save的源码

def save(self, **kwargs):
    
   # 如果self.instance 没有值就会执行self.create(validated_data)
    if self.instance is not None:  
        self.instance = self.update(self.instance, validated_data)
        assert self.instance is not None, (
            '`update()` did not return an object instance.'
        )
    else:
        self.instance = self.create(validated_data)
        assert self.instance is not None, (
            '`create()` did not return an object instance.'
        )

    return self.instance

self.create(validated_data)

所以这里才会报这个错

def create(self, validated_data):
    raise NotImplementedError('`create()` must be implemented.')

 解决: 在我们的serilizer中重写create方法

def create(self, validated_data):
    res = Book.objects.create(**validated_data)
    return res

 修改

 1. 获取pk值

2. 前端传递数据,从request.data取出来

class BookDetailView(APIView):
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

 改写put方法

1.instance要修改的对象
2.validated_data 校验过后的数据

def update(self, instance, validated_data):
    
    instance.name = validated_data.get('name')
    instance.price = validated_data.get('price')
    instance.publish = validated_data.get('publish')
    instance.save()
    return instance

delete直接删

class BookDetailView(APIView):
        def delete(self, request, pk):
            book = Book.objects.filter(pk=pk)
            book.delete()
            return Response()

get查询

查询单条

class BookDetailView(APIView):
   
        def get(self, request, pk):
            book = Book.objects.all().filter(pk=pk).first()
            bookSerializer = BookSerializer(instance=book,)
            print(book)
            res = bookSerializer.data
            return Response(res)

或者

class BookView(APIView):  
    def get(self, request, pk):
        book = Book.objects.all().filter(pk=pk).first()
        bookSerializer = BookSerializer(instance=book)
        print(book)
        res = bookSerializer.data
        return Response(res)

查询多条

class BookDetailView(APIView):
        def get(self, request, pk):
            book = Book.objects.all().filter(pk=pk)
            bookSerializer = BookSerializer(instance=book,many=True)
            print(book)
            res = bookSerializer.data
            return Response(res)

 

posted @ 2022-09-26 21:57  没错,干就完了!  阅读(138)  评论(0)    收藏  举报