drf十大接口

提示:模型表models.py在文末

单查&群查

简单情况下,查询一条数据和查询多条数据是有两条URL(带pk的和不带pk的),对应两个APIView视图类。

复杂一点,可以将两个APIView视图类合并在一起,两条URL对应一个统一的URL。在视图类中通过判断pk值是否存在来选择单查还是群查。

其实,如果这个视图仅仅做查询接口使用,那么可以继承drf提供的ReadOnlyModelViewSet,这样就不需要再重复写查询接口的代码。并且经过简单配置就可以使用分页功能。

# 基于APIView的群查和单查
class BookAPIView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if not pk:
            # 过滤出没有被删除的数据
            book_list = models.Book.objects.filter(is_delete=False).all()
            ser_obj = ser.BookModelSerializer(book_list, many=True)
            return Response(ser_obj.data)
        else:
            try:
                book_obj = models.Book.objects.filter(is_delete=False).get(pk=pk)
            except Exception:
                return Response(data={'code': 1001, 'detail': '查询的数据不存在'})

            ser_obj = ser.BookModelSerializer(book_obj)
            return Response(ser_obj.data)
# urls.py
path('books/', views.BookAPIView.as_view()),
re_path(r'books/(?P<pk>\d+)/', views.BookAPIView.as_view()),


# 基于ReadOnlyModelViewSet的十大接口
class BookReadOnlyModelViewSet(ReadOnlyModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = ser.BookModelSerializer
    pagination_class = ApiPageNumberPagination      # 分页器
# urls.py
    path('books3/', views.BookReadOnlyModelViewSet.as_view(actions={'get': 'list',})),
    re_path(r'books3/(?P<pk>\d+)/', views.BookReadOnlyModelViewSet.as_view(actions={'get': 'retrieve'})),

单增&群增

增加数据提交POST请求,可以增加一条记录,也可以增加多条记录,对应的URL都是一条。

简单点,继承APIView视图类,手动实现post方法,在post方法内分情况判断是增加一条记录还是增加多条记录。

当新增一条记录时,request.data的中的数据是一个字典,当增加多条记录时request.data中的数据是一个列表。

# views.py
class BookAPIView(APIView):
    def post(self, request, *args, **kwargs):
        recv_data = request.data
        if isinstance(recv_data, dict):
            ser_obj = ser.BookModelSerializer(data=recv_data)
            ser_obj.is_valid(raise_exception=True)
            ser_obj.save()
            return Response(ser_obj.data)
        elif isinstance(recv_data, list):
            ser_obj = ser.BookModelSerializer(data=recv_data, many=True)
            ser_obj.is_valid(raise_exception=True)
            ser_obj.save()
            return Response(ser_obj.data)
        else:
            return Response(data={'code': 1002, 'detail': '提交的数据格式不正确'})
        
"""
# 新增一条记录时,request.data应该是一个字典对象,里面包含了新增记录需要的所有必填字段数据
	-此时直接使用模型类序列化器生成一个对象,数据校验合格后,调用模型类序列化器对象的save方法;
    -内部会调用模型对象的create方法,完成新增一条记录。【模型类帮我么实现了create方法和update方法】
# 新增多条记录时,request.data应该是一个列表,列表里面包含了多个字典,字典内是每一条记录需要的字段数据
	-此时传入模型类序列化器实例化时在传入列表的同时,需要指定参数many=True
    -这是实例化的对象不再是模型类对象,而是一个drf内置的ListSeralizer对象,该类帮我们实现了create批量增加
    -调用ListSeralizer对象的save方法时,内部会循环调用每一个模型类对象的create方法,实现批量增加多条记录
"""

单改&群改

修改一条数据,一把在url中指定要修改数据的id值,在请求体中携带新数据。

批量修改数据时,没有在url中体现多个对象的id值,而是在请求体中携带每个要修改记录的id值即新数据。

和批量增加多条记录类似,批量修改多条记录时,前段传过来的数据格式也要有限制,请求体中传过来的数据必须提现要修改对象的id值。

然后使用模型类序列化器实例化对象时传入的参数instance是一个包含对个模型对象的列表,data是移除了id值的待修改的新数据。同时传入参数many=True,内部会实例化一个ListSerializer对象;

调用该对象的save方法,会调用其update方法,但是这个方法是需要被重写的,不然会报错。

因此,此处需要自定义一个继承了ListSerializer对类,并重写update方法。该方法内循环调用模型类序列化器对象的update方法,是想批量修改多条记录。

默认情况下many=True将创建一个drf内置的ListSerializer对象,但该类没有update方法,因此我们需要指定实例化我们自己继承ListSerializer子类的对象。

指定的方法很简单,就是模型类序列化器的meta类中指定即可,list_serializer_class = BookListSerializer

另外,不论是单改还是批量修改,修改一条数据时都可以指定全部修改还是局部修改,使用参数partial=True,内部本质是让每个字段都为required=False

# views.py
def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:
            try:
                book_obj = models.Book.objects.filter(is_delete=False).get(pk=pk)
                ser_obj = ser.BookModelSerializer(instance=book_obj, data=request.data, partial=True)
                ser_obj.is_valid(raise_exception=True)
                ser_obj.save()
                return Response(ser_obj.data)
            except Exception:
                return Response(data={'code': 1004, 'msg':'出错了', 'detail': '修改的对象不存在'})
        else:
            # 群修改, request.data的数据格式,[book1, book2, ...]
            # 图书id列表
            book_id_list = [item.pop('id') for item in request.data]
            # 图书对象列表
            book_obj_list = models.Book.objects.filter(is_delete=False).filter(id__in=book_id_list)
            # ser_obj是自定义的ListSerializer对象,该类中需要重写update方法
            # partial=True表示局部修改,即部分修改,本质上是让每个字段required=False
            ser_obj = ser.BookModelSerializer(instance=book_obj_list, 
                                              data=request.data, many=True, partial=True)
            ser_obj.is_valid(raise_exception=True)
            ser_obj.save()
            return Response(ser_obj.data)
        
# ser.py
class BookListSerializer(serializers.ListSerializer):
	# 自定义ListSerializer并重写update方法
    def update(self, instance, validated_data):
        """
        # instance是待修改的图书列表,validated_data是提交的修改数据列表
        # 返回一个修改后的对象列表,child是BookModelSerializer序列化器对象
        # 循环调用该序列化器对象的update方法,返回修改后的图书对象
        """
        return [
            self.child.update(instance[index], validated_data=item)
            for index, item in enumerate(validated_data)
        ]
    
class BookModelSerializer(serializers.ModelSerializer):
    publish_id = serializers.IntegerField(write_only=True)
    authors_id = serializers.ListField(write_only=True, source='authors')
    publish = serializers.SerializerMethodField(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)

    def get_publish(self, instance):
        publish = instance.publish
        return {'id': publish.pk, 'name': publish.name, 'email': publish.email}

    def get_authors(self, instance):
        authors = instance.authors.all()
        return [
            {'id': author.pk, 'name': author.name, 'sex': author.sex}
            for author in authors
        ]

    class Meta:
        model = models.Book
        # 群修改操作,指定自定义ListSerializer类,并重写该类的update方法
        list_serializer_class = BookListSerializer

        fields = (
            'id', 'name', 'price',
            'publish', 'authors',
            'publish_id', 'authors_id',
        )
        # depth = 1

单删&群删

实际开发中,一般不会将数据删除掉,而是使用一个布尔字段标识该字段是否被删除。

删除操作其实用不到序列化器,但是需要注意数据的删除操作,不是真是的删数据而是修改是否被删除字段。

# views.py
# 基于APIView的十大接口
class BookAPIView(APIView):
    def delete(self, request, *args, **kwargs):
            pk = kwargs.get('pk', None)
            pks = []
            if pk:
                pks.append(pk)
            else:
                pks = request.data.get('pks', None)

            ret = models.Book.objects.filter(is_delete=False).filter(id__in=pks).update(is_delete=True)
            msg = '删除成功' if ret else '删除的数据不存在'
            return Response(data=msg)

# 单删是通过url朝后端发送待删除数据的id值,群删则捅咕请求体将群删的id列表传给后端。        
# 此处为了统一单删和多删的处理方式,将单删的记录的pk值也放在了pks这个列表中
# 删除操作,使用了orm的双下划线模糊查询__in来统一批量修改删除字典的不布尔值。

基于ModelViewSet的十大接口

基于ModelViewSet的十大接口需要需要重写一些方法,比如群增的post方法和群改的put方法。

在群增时,千万记得记得不要在url中通过actions参数指定post请求的处理方式为create()

在群改时,put请求直接来到自己写的put方法中,坑【url中actions参数】

删除时,需要重写perform_destroy方法,不是真是的删数据库数据而是修改布尔字段。

# views.py
class BookModelViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = ser.BookModelSerializer
    pagination_class = ApiPageNumberPagination      # 分页器

    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset.filter(is_delete=False).all()

    def perform_destroy(self, instance):
        instance.is_delete = True
        instance.save()

    def post(self, request, *args, **kwargs):
        recv_data = request.data
        if isinstance(recv_data, dict):
            return super().create(request, *args, **kwargs)
        elif isinstance(recv_data, list):
            ser_obj = ser.BookModelSerializer(data=recv_data, many=True)
            ser_obj.is_valid(raise_exception=True)
            ser_obj.save()
            return Response(ser_obj.data)
        else:
            return Response(data={'code': 1002, 'detail': '提交的数据格式不正确'})

    def put(self, request, *args, **kwargs):
        # 群修改是执行次操作
        # 群修改, [book1, book2, ...]
        # 图书id列表
        book_id_list = [item.pop('id') for item in request.data]
        # 图书对象列表
        book_obj_list = self.get_queryset().filter(id__in=book_id_list)
        # ser_obj是自定义的ListSerializer对象,该类中需要重写update方法
        print(request.data)
        ser_obj = self.get_serializer(instance=book_obj_list, data=request.data, many=True, partial=True)
        ser_obj.is_valid(raise_exception=True)
        ser_obj.save()
        return Response(ser_obj.data)

    def delete(self, request, *args, **kwargs):
        pks = request.data.get('pks', None)
        ret = self.get_queryset().filter(id__in=pks).update(is_delete=True)
        msg = '删除成功' if ret else '删除的数据不存在'
        return Response(data=msg)

# urls.py  
path('books3/', views.BookModelViewSet.as_view(actions={'get': 'list',})),
re_path(r'books3/(?P<pk>\d+)/', views.BookModelViewSet.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),    

模型表

from django.db import models

class BaseModel(models.Model):
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)
    is_delete = models.BooleanField(default=False)

    class Meta:
        abstract = True		# 基表,通过该参数指定告知orm不要创建该表


class Book(BaseModel):
    # 一张表中只能有一个自增字段
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, verbose_name='图书名')
    price = models.DecimalField(max_digits=6, decimal_places=2)
    publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING, db_constraint=False)
    authors = models.ManyToManyField(to='Author', db_constraint=False)

    class Meta:
        verbose_name_plural = '图书表'

    def __str__(self):
        return self.name


class Publish(BaseModel):
    name = models.CharField(max_length=32, verbose_name='出版社')
    email = models.EmailField()

    class Meta:
        verbose_name_plural = '出版社'

    def __str__(self):
        return self.name


class Author(BaseModel):
    name = models.CharField(max_length=32, verbose_name='出版社')
    sex = models.IntegerField(choices=((1, '男'), (2, '女')))
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, db_constraint=False)

    class Meta:
        verbose_name_plural = '作者表'

    def __str__(self):
        return self.name

    
class AuthorDetail(BaseModel):
    phone = models.CharField(max_length=11)
    age = models.IntegerField()

    class Meta:
        verbose_name_plural = '作者详情表'
posted @ 2020-07-14 22:15  the3times  阅读(522)  评论(0编辑  收藏  举报