DRF序列化组件

目录

-DRF

--定义

--安装

--基本使用

--drf中的request

-序列化组件

--基本介绍

--基本使用

---①Serializer

---②ModelSerializer

---序列化组件常用参数

---序列化组件修改数据

---序列化组件删除数据

---序列化组件新增数据

---source使用

 --- SerializerMethodField使用

 --- 局部钩子和全局钩子

--进阶使用

--- 基于GenericAPIView类写views.py中的五个接口函数

--- 基于GenericAPIView类和五个扩展类写views.py中的五个接口函数

--- 基于GenericAPIView类的9个子类写views.py中的五个接口函数

--- 基于ModelViewSet类写views.py中的五个接口函数

--- 自动生成路由

--- action装饰器使用

总结


-DRF

--定义

DRF(Django REST Framework)是一个建立在Django基础之上的Web应用开发框架,可以快速的开发REST API接口应用

在REST Framework中,提供了序列化器Serializer的定义,可以帮助我们简化序列化和反序列化的过程

不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。

同时,REST Framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持;

还提供了一个API的Web可视化界面来方便查看测试接口

核心功能:缩减编写api接口的代码

--安装

pip install djangorestframework

--基本使用

drf使用的都是CBV的方式

1、在settings.py中注册
INSTALLEN_APPS = [
    'rest_framework',    # 固定注册方式
]
2、在models.py中书写模型表
class Book(models.Model):
    id = models.AutoField(primary_key = True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    author = models.CharField(max_length=16)
 执行数据库迁移命令
3、基于CBV方式写视图函数
from rest_framework.views import APIView
class BookApiView(APIView):
    def get(self, request,*args,**kwargs): #可接收多参数,下同
        return HttpResponse('get请求')
    def post(self, request):
        # print(request.data) # 当post请求过来时,不管什么格式的数据(json,urlencoded)都可在request.data中接收
        return HttpResponse('post请求')
    …
4、注册路由
path('/books', views.BookApiView.as_view()), # 要加括号!
基本使用

然后在postman上就可以通过http://127.0.0.1:8000/books/通过请求方式的不同调用不同的视图函数

path('books/', views.BookApiView.as_view()),

def get(self, request, pk=None): # 或用**kwargs判断
    # 序列化 先获得待查询pk的queryset对象
    if pk is None:
       book_obj = models.Book.objects.all()
       # BookSerializers实例化生成序列化对象
       book_serializers_obj = BookSerializers(book_obj, many=True)
查询所有

--drf中的request

- request.data
"""
    新的request有一个新的属性:data
     - data是post请求携带的数据,返回结果为字典的形式
     - 在django中我们知道不同的编码格式存储在不同的方法中,如request.POST获取urlencoded编码格式的数据;request.body获取json格式的数据
     - 而在drf中,无论什么编码格式,只要是post提交的数据,都在request.data中
    所以以后取post提交的数据不要从request.POST中去了,要从request.data中取
"""
- request.query_params      # 就是request._request.GET
"""取get请求的数据不要从request.GET中取了,而是从request.query_params中取"""
- request.FIELS     
'''上传的文件'''
request对象:
    from rest_framework.request import Request
    点击Request查看源码:
        def __init__(self, request, parsers=None, 
                authenticators=none,negotiator=None,
                parser_context=None):
            # 二次封装django的request对象,将其作为drf的_request属性
            self._request = request
            …
        …
response对象:
    from rest_framework.response import Response
    点击Response查看源码:
        def __init__(self, 
                data=None,status=None,template_name=None, headers=None,exception=None, content_type=None):
            …
        data:要返回的数据(字典)
        status:返回状态码,默认200
            from rest_framework import status
            里面定义了很多状态码常量
eg:return Response(data,status=status.HTTP_200_OK)
DRF中的request对象和response对象

-序列化组件

--基本介绍

DRF自带一个序列化组件Serializer(类)

作用:

        1.序列化:Serializer会把模型对象(表对象,QuerySet对象)转换成字典,经过response后变为json字符串

        2.反序列化:把客户端发来的数据经过request后变为字典(request.data),Serializer可以把字典转为模型对象

        3.完成数据校验功能

--基本使用

---①Serializer

1、新建一个.py序列化组件文件(见名知意),在该文件下写序列化类,继承Serializer
    from rest_framework import serializers
    class BookSerializers(serializers.Serializer):
        ...
2、在类中写要序列化的字段
    # 写要序列化的字段
    id = serializers.CharField()
    name = serializers.CharField(max_length=16,min_length=4)
    # book_name = serializers.CharField(max_length=16,min_length=4, source='get_book_name')
    price = serializers.CharField()
    author = serializers.CharField()
3、写路径(这以查单个为例,发get请求,带主键为筛选条件)
    re_path('books/(?P<pk>\d+)/', views.BookApiView.as_view())
4、在视图类中使用
    from app01.mySerializer1 import BookSerializers
    from rest_framework.response import Response
    class BookApiView(APIView):
        def get(self, request, pk):
            # 序列化 先获得待查询pk的queryset对象
            book_obj = models.Book.objects.filter(pk=pk).first()
            # BookSerializers实例化生成序列化对象
            book_serializers_obj = BookSerializers(book_obj)
            # 固定返回格式 book_serializers_obj.data是一个字典
            return Response(book_serializers_obj.data)
        """
        Response是drf提供的响应对象,如果不用Response,就用JsonResponse,不过就没有drf提供的那么多功能了(比如浏览器查返回好看界面)
        """
Serializer

---②ModelSerializer

'''序列化类也可以不继承Serializer,而是继承ModelSerializer(模型序列化组件),在Meta中对应模型表及规定序列化字段'''
from rest_framework.serializers import ModelSerializer
from app01.models import Book
 
class BookModelSerializer(ModelSerializer): # 类名任取
    class Meta:
        model = Book         # 对应models.py中的模型表
        fields = '__all__'   # 序列化所有字段
        # fields = ('name', 'price')    # 序列化指定字段 列表or元组
        # exclude = ('name', )          # 除外均序列化 列表or元组
        # read_only_fields = ['id']     # 设置只读字段
        '''extra_kwargs = [             # 设置其它
            'price' : {'write_only' : True},
        ]'''
        # depth = 1    # 深度:深一层(外键深入的意思,=1表示把关联了一层的外键字段也显示出来) 非常耗费效率,不能控制深入后想要查询的字段,不建议使用,写多层不会报错,有几层就深入几层
 
视图函数中使用方法一样,只是不需要重写create和update方法了。其余使用相同
ModelSerializer

---序列化组件常用参数

----常用字段类型

 ----常用选项参数

 ----通用参数

read_only    默认false,表明该字段仅读仅用于序列化输出。为true时postman中可以看到该字段,修改时不需要传该字段
write_only   默认false,表明该字段仅用于反序列化输出。为true时postman看不到该字段,修改时该字段需要传
 
required          表明该字段在反序列化时必须输入,默认true
default           反序列化时使用的默认值
allow_null        默认false,是否允许传入None
validators        使用的验证器
error_messages    包含错误代码和错误信息的字典
通用参数

---序列化组件修改数据

当发送PUT请求时,修改数据

def put(self, request, pk):
    back_dict = {'code':1000, 'msg':'成功'}
    book_obj = models.Book.objects.filter(pk=pk).first()
    # 实例化生成对象时传入修改数据
    # book_serializers_obj = BookSerializers(book_obj, request.data)
    book_serializers_obj = BookSerializers(instance=book_obj, data=request.data)
    # 修改数据 验证修改后的数据是否合适 合适才能修改
    if book_serializers_obj.is_valid():
        # 保存数据
        book_serializers_obj.save() # 会报错 需重写updata()方法
        back_dict['datas'] = book_serializers_obj.data
    else:
        back_dict['code'] = 1001
        back_dict['msg'] = '失败'
        back_dict['error'] = book_serializers_obj.errors
    return Response(back_dict)
序列化组件修改数据

当执行book_serializers_obj.save()保存修改后的数据时,会报错:

NotImplementedError: `update()` must be implemented.

说update()方法必须重写,点击save()查看update()源码得:

def update(self, instance, validated_data):
    raise NotImplementedError('`update()` must be implemented')

原来是作者定义时自动把update()写为raise异常

所以我们想要成功修改数据就必须重写update()方法(在定义的序列化类里书写

# 重写update方法
def update(self, instance, validated_data):
    """
    :param instance: 就是当前的queryset对象
    :param validated_data: 校验成功的数据
    """
    instance.id = validated_data.get('id')
    instance.name = validated_data.get('name')
    instance.price = validated_data.get('price')
    instance.author = validated_data.get('author')
    instance.save()
    return instance
重写update方法

之后再发送PUT请求就能修改数据成功了

---序列化组件删除数据

当发送DELETE请求时,删除数据

def delete(self, request, pk):
    # 删除数据无需序列化和反序列化,直接删除即可
    models.Book.objects.filter(pk=pk).delete()
    # 删除成功返回一个空,如果没删除成功会自动抛异常返回一个空
    return Response()
序列化组件删除数据

---序列化组件新增数据

当发送post请求时,可以新增数据

def post(self, request):
    back_dict = {'code': 1000, 'msg': '成功'}
    book_serializers_obj = BookSerializers(data=request.data)  # 如果要新增就不需要传instance,按关键字传
    if book_serializers_obj.is_valid():
        book_serializers_obj.save()
        back_dict['data'] = book_serializers_obj.data
    else:
        back_dict['code'] = 1001
        back_dict['msg'] = '失败'
        back_dict['error'] = book_serializers_obj.errors
    return Response(back_dict)
序列化组件新增数据

同理,新增数据需要重写create()方法(在定义的序列化类中书写)

def create(self, validated_data): # validated_data为用户上传的数据
    # 通过表对象保存,这样就知道了保存到那张表中
    instance = models.Book.objects.create(**validated_data)  # 打散保存
    # 返回
    return instance
重写create方法
每次与前端交互时都需定义一个back_dict字典,太麻烦
可自己新建一个.py文件封装一个对象,实现传字典的功能
    class MyResponse():
        def __init__(self):
            self.code = 1000
            self.msg = '成功'
    
        @property
        def get_dict(self):
        return self._ _dict_ _

此时:
# 发送post请求 新增数据
def post(self, request, *args, **kwargs):
    response_obj = MyResponse()
    book_serializers_obj = BookSerializers(data=request.data)  # 如果要新增就不需要传instance,按关键字传
    if book_serializers_obj.is_valid():
        book_serializers_obj.save()
        response_obj.data = book_serializers_obj.data    
    else:
        response_obj.code = 1001
        response_obj.msg = '失败'
        response_obj.error = book_serializers_obj.errors
    return Response(response_obj.get_dict)
补充

---source使用

在序列化组件时,序列化类中的字段必须与模型表内的字段是对应的

如果序列化时不对应,请求来访问时就会报错。如果想使用别的字段名①,就可以用source

book_name = serializers.CharField(source='name')
# source这可以把它看做Book对象(instance),source='name'就<==>instance.name获取book对象的属性名name传给book_name

source也可以对应模型表中的方法②(方法返回结果是什么,字段对应结果就是什么)

models.py
class Book(models.Model):
    ...
 
    def get_book_name(self):
        return '四大名著:' + self.name
 
序列化类
class BookSerializers(serializers.Serializer):
    book_name = serializers.CharField(source='get_book_name')

                                    

 当该模型表下还有与其他表相关联的外键字段时,source还支持跨表查询以使外键字段显示相应数据                 主要用来做一对多,多对多的字段返回

models.py
class Book(models.Model):
    ...
    # 与出版社关联
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
 
序列化类
class BookSerializers(serializers.Serializer):
    ...
    publish_addr = serializers.CharField(source='publish.address')

                            

 --- SerializerMethodField使用

SerializerMethodField能帮助拿到关联表所有字段的值(如拿到出版社的详细信息)

使用SerializerMethodField就必须指定一个get_字段名的方法,该字段名是SerializerMethodField方法需要序列化的字段

序列化类中
class BookSerializers(serializers.Serializer):
    。。。
    # SerializerMethodField使用 必须搭配一个函数:get_字段名
    publish = serializers.SerializerMethodField()
    def get_publish(self, instance):
        # instance即为book对象
        publish_obj = instance.publish # 跨表到publish对象
        datas_list = [{'name':publish_obj.name,'addr':publish_obj.address}]
        return datas_list
SerializerMethodField使用

                             

 --- 局部钩子和全局钩子

class BookSerializers(serializers.Serializer):
    。。。
 
    # 局部钩子 validate_字段名 data为字段对应数据(名字任取)
    def validate_price(self, data):
        if float(data) >= 0:
            return data
        else:
            raise ValidationError('价格非负')
 
    # 全局钩子 attrs为全部合格数据(名字任取)
    def validate(self, attrs):
        name = attrs.get('name')
        author = attrs.get('author')
        if name == author:
            raise ValidationError('书籍名和作者名字相同')
        else:
            return attrs
局部钩子和全局钩子

--进阶使用

--- 基于GenericAPIView类写views.py中的五个接口函数

效果等价,只是比基于APIView写的代码少。∵都继承APIView,但有了更多方法

models.py
    class Publish(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        address = models.CharField(max_length=64)
        def __str__(self):
            return self.name
urls.py
    # 基于GenericAPIView的关于Publish
    path('publishes/', views.PublishGenericAPIView.as_view()),
    re_path('publishes/(?P<pk>\d+)/', views.PublishGenericAPIViewByPK.as_view()),
views.py
    from rest_framework.generics import GenericAPIView
    class PublishGenericAPIView(GenericAPIView):
        queryset = models.Publish.objects
        #queryset = models.Publish.objects跟上面等价的
        serializer_class = PublishSerializers
        # 发送get请求查所有
        def get(self, request):
            publish_obj = self.get_queryset()
            publish_serializers_obj = self.get_serializer(publish_obj,many=True)
            return Response(publish_serializers_obj.data)
        # 发送post请求 新增数据
        def post(self, request):
            publish_serializers_obj = self.get_serializer(data=request.data)        
            if publish_serializers_obj.is_valid():
                publish_serializers_obj.save() # 需重写create方法吗?——要
                return Response(publish_serializers_obj.data)
            else:
                return Response({'msg':'数据不合要求'})
    class PublishGenericAPIViewByPK(GenericAPIView):
        queryset = models.Publish.objects
        serializer_class = PublishSerializers
        # get请求查单个数据
        def get(self, request, pk):
            publish_obj = self.get_object()
            publish_serializers_obj = self.get_serializer(publish_obj)
            return Response(publish_serializers_obj.data)
        # PUT请求修改数据
        def put(self, request, pk):
            publish_obj = self.get_object()
            publish_serializers_obj = self.get_serializer(publish_obj,data=request.data)
            if publish_serializers_obj.is_valid():
                publish_serializers_obj.save()  # 需重写updata()方法吗?——要
                return Response(publish_serializers_obj.data)
            else:
                return Response({'msg':'修改后的数据不合格'})
        # 发送delete请求 删除单个
        def delete(self, request, pk):
            self.get_object().delete()
            return Response({'msg':'删除成功'})
基于GenericAPIView类写views.py中的五个接口函数

--- 基于GenericAPIView类和五个扩展类写views.py中的五个接口函数

urls.py
    path('publishes/', views.PublishGenericAPIView5others.as_view()),
    re_path('publishes/(?P<pk>\d+)/',  
                                           views.PublishGenericAPIView5othersByPK.as_view()),
views.py
    from rest_framework.mixins import ListModelMixin,CreateModelMixin,\                                
                                        UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin
    class PublishGenericAPIView5others(GenericAPIView,ListModelMixin,CreateModelMixin):
        queryset = models.Publish.objects
        serializer_class = PublishSerializers
        def get(self, request):
            return self.list(request) # 查所有
        def post(self, request):
            return self.create(request) #
    class PublishGenericAPIView5othersByPK(GenericAPIView,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin):
        queryset = models.Publish.objects
        serializer_class = PublishSerializers
        def get(self, request, pk):
            return self.retrieve(request,pk) # 查单个
        def put(self, request, pk):
            return self.update(request,pk) #
        def delete(self, request, pk):
            return self.destroy(request,pk) #
基于GenericAPIView类和五个扩展类写views.py中的五个接口函数

--- 基于GenericAPIView类的9个子类写views.py中的五个接口函数

urls.py
    path('publishes/', views.PublishGenericAPIView9children.as_view()),
    re_path('publishes/(?P<pk>\d+)/', 
                                             views.PublishGenericAPIView9childrenByPK.as_view()),
views.py
    # 基于GenericAPIView的9个子类 需要用哪个就导哪个 有些类是含两个功能
    from rest_framework.generics import ListAPIView,UpdateAPIView,\
        CreateAPIView,DestroyAPIView,ListCreateAPIView,RetrieveAPIView,\
        RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView
    # 查所有 新增 两个使用方式等价
    # class PublishGenericAPIView9children(ListAPIView, CreateAPIView):
    class PublishGenericAPIView9children(ListCreateAPIView):
        queryset = models.Publish.objects
        serializer_class = PublishSerializers
    class PublishGenericAPIView9childrenByPK(UpdateAPIView,DestroyAPIView,RetrieveAPIView):
        queryset = models.Publish.objects
        serializer_class = PublishSerializers
基于GenericAPIView类的9个子类写views.py中的五个接口函数

--- 基于ModelViewSet类写views.py中的五个接口函数

urls.py
    path('publishes/', views.PublishModelViewSet.as_view({'get':'list','post':'create'})),
    re_path('publishes/(?P<pk>\d+)/', views.PublishModelViewSet.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})      # 用关键字命名最好
views.py
    # 基于ModelViewSet写5个接口
    from rest_framework.viewsets import ModelViewSet
    class PublishModelViewSet(ModelViewSet):
        queryset = models.Publish.objects
        serializer_class = PublishSerializers
基于ModelViewSet类写views.py中的五个接口函数

--- 自动生成路由

# 第一步 导入routers中的两个类
from rest_framework.routers import SimpleRouter,DefaultRouter
# 第二步 实例化得到对象
router = SimpleRouter() # 自动生成两条路由 一般用它
# router = DefaultRouter() # 自动生成六条路由 不推荐→
# 第三步 注册
# router.register('前缀', 继承ModelViewSet的视图类, [‘别名’])
router.register('publishes', views.PublishModelViewSet) # 不加斜杠/
"""
print(router.urls)
    [<URLPattern '^publishes/$' [name='publish-list']>, 
    <URLPattern '^publishes/(?P<pk>[^/.]+)/$' [name='publish-detail']>]
"""
# 第四步 将自动生成路由加入路由列表中 在urlpatterns下书写
urlpatterns += router.urls
自动生成路由

--- action装饰器使用

装饰器action用于给在继承ModelViewSet的视图类中自定义的函数也添加路由

'''views.py'''
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
class PublishModelViewSet(ModelViewSet):
    queryset = models.Publish.objects
    serializer_class = PublishSerializers
 
    @action(methods=['get','post'], detail=False)
    """
    methods是访问此路由时来的可执行请求 不区分大小写 内部自动.lower()
    detail布尔类型 为False时自动路由为publishes/get_all/$ [name='publish-get-all']
    """
    def get_all(self, request):
        # print(request.method) # 可通过request.method来分别做get请求和post请求的事!
        publish_obj = self.get_queryset()
         # publish_obj = self.get_queryset()[:10] # 截取,前十条
        publish_ser_obj = self.get_serializer(publish_obj, many=True)
        return Response(publish_ser_obj.data)
 
    @action(methods=['get'], detail=True)
    """
    detail为True时 自动生成路由匹配为publishes/(?P<pk>[^/.]+)/get_1/$ [name='publish-get-1']           注意pk在中间
    """
    def get_1(self, request, pk):
        publish_obj = self.get_object()
        publish_ser_obj = self.get_serializer(publish_obj)
        return Response(publish_ser_obj.data)
action装饰器使用

总结

一般写序列化组件用APIView原生,可扩展功能多

GenericAPIView用于操作数据库时方便


 

 
posted @ 2022-12-12 21:21  weer-wmq  阅读(43)  评论(0编辑  收藏  举报