首页 > 其他分享 >drf 序列化与反序列化 接口

drf 序列化与反序列化 接口

时间:2023-02-01 23:23:44浏览次数:79  
标签:request self APIView 接口 import 序列化 data drf

基于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()),

image

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()),

image

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查看源码:
    发现左侧有很多方法看重点的几个

image

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提供了两个类:SerializerModelSerializer

基于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()),

image

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())

image

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就不用管前端的编码方式是什么 三种都可以传

image

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())

image

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': '删除成功'})

image

反序列化的数据校验功能

# 序列化类的反序列化有个数据校验功能(类似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

image

全局钩子:要求书名跟出版社名字不能一致

    # 全局钩子
    def validate(self, attrs):
        # attrs 校验后的数据
        if attrs.get('name') == attrs.get('publish'):
            # 校验不通过,主动抛异常
            raise ValidationError('书名和出版社名字不能一致')
        else:
            return attrs

image

标签:request,self,APIView,接口,import,序列化,data,drf
From: https://www.cnblogs.com/lvqingmei/p/17084477.html

相关文章