DRF序列化组件实现十大接口

冷暖自知 提交于 2020-03-05 20:39:27

基表的概念

abstract = True

# 基表
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)
    class Meta:
        # 基表,抽象类
        abstract = True
        
class Book(BaseModel):
    pass

class Publish(BaseModel):
    pass

表关系的建立

通常我们在实际开发项目中我们的数据库各种表是不建立外键关联表的,是为什么呢?

因为我们在建立了外键关系之后,随着业务发展,表越来越多,外间关系越来越复杂,假如有一天表字段可能有问题,需要重构什么的,需要删除掉,那会发现删不掉了,因为和其他很多表都建立了外键关系,这时候我们的数据库断开关联表关系的优点就体现出来了:

  • 断关联表不会影响连表的查询操作
  • 还有助于提升连表增删改操作的效率
  • 最重要的一点是方便后期的扩展与重构
  • 那么他的缺点就是数据库本身没有连表的检测,容易出现脏数据,需要通过我们严格的代码逻辑来避免脏数据的产生。

我们来举个例子,比如说图书管理系统:

  • Book 和 Publish 一对多,外键在多的一方
  • Book 和 Author 多对多,外键在查询频率高的一方
  • Author 和 AuthorDetail,一对一,外键根据实际情况建在合理的情况,一般在查询频率低的地方
class Book(BaseModel):
    name = models.CharField(max_length=16)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.ForeignKey(to='Publish',
                                related_name='books', db_constraint=False,
                                on_delete=models.DO_NOTHING,null=True)
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)


class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)


class Author(BaseModel):
    name = models.CharField(max_length=16)


class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    author = models.OneToOneField(to='Author', related_name='detail',
                                  db_constraint=False, on_delete=models.CASCADE)

外键字段属性解释

  • related_name:可以在外键中设置外键反向查询的字段,正向查询按字段,反向查询按related_name值

  • db_constraint:在外键字段中控制表是否关联,默认为True表示关联,设置False表示断开关联。

  • on_delete:在外键中必须设置,表示级联关系

    在Django1.X版本下,系统默认提供的是models.CASCADE

    在Django2.X版本下,必须手动明确on_delete参数(有如下可选的参数)

    注:在多对多关系字段中不能设置on_delete级联关系,默认为级联,如要处理级联关系,需手动明确关系表,处理关系表中的多个外键。

    • CASCADE:默认值,表示级联,比如作者和作者详情关联,作者删除,详情也删除
    • DO_NOTHING:外键不会被级联,什么也不做,比如作者与书关联,作者删除,书还在
    • SET_DEFAULT:外键设置级联后,删除后外键字段设置为默认值,必须配合default参数使用,比如部门与员工关联,部门解散后,员工进入待定默认部门
    • SET_NULL:外键设置级联后,删除后外键字段设置为Null,必须配合Null=True参数使用,比如部门与员工关联,部门解散后,员工无所属部门为Null

DRF中Response类二次封装

from rest_framework.response import Response

class APIResponse(Response):
    def __init__(self,status=0,msg='ok',results=None,http_status=None,
                 headers=None,exception=False,content_type=None,**kwargs):
        # 组织data响应体字典
        data = {
            'status':status,
            'msg':msg,
        }
        # results只要不是空都是数据,包括0,false,条件不能写if results
        if results is not None:
            data['results'] = results
        # 将kwargs中额外的k-v数据添加到data中
        data.update(**kwargs)

        # data=None, status=None,template_name=None, headers=None,exception=False, content_type=None
        super().__init__(data=data,status=http_status,headers=headers,exception=exception,content_type=content_type)

连表查询

depth配置查询深度

  • 自动查询深度
  • 会把需要序列化出来的外键字段关联的其他表中所有信息查询出来,前提是外键字段参与序列化
  • 可设置查询几层的深度

depth = 5

class PublishModelSerializer(serializers.ModelSerializer):
    #
    # books = BookModelSerializer(many=True)
    class Meta:
        model = models.Publish
        fields = ['name','address','books_list']
        # fields = ['name','books']
        # 设置自动查询深度,会把需要序列化出来的外键字段关联的其他表中所有信息查询出来
        # 可设置几层的深度
        depth = 5

插拔式自定义序列化字段连表查询

  • 设置fields参与序列化的字段
  • 在models模型类中配置方法

ModelSerializer序列化类

class PublishModelSerializer(serializers.ModelSerializer):
    #
    # books = BookModelSerializer(many=True)
    class Meta:
        model = models.Publish
        fields = ['name','address','books_list']

models模型类中配置

class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)

    @property
    def books_list(self):
        book_list = []
        books = self.books.all()
        for book in books:
            book_dict = {
                'name':book.name,
                'price':book.price
            }
            book_list.append(book_dict)
        return book_list

子序列化

  • 只能在序列化类中使用
  • 字段名必须是外键名
  • 在外键关联数据是多条数据的时候,需要明确many=True
  • 单向操作,作为子序列化的类必须写在序列化类的上方

路由配置:

url(r'^v1/publishes/$',views.PublishView.as_view()),

视图配置:

# 出版社群查
class PublishView(APIView):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('pk')
        if pk:
            publish_obj = models.Publish.objects.filter(pk=pk,is_delete=False).first()
            if not publish_obj:
                return Response({'status': 1, 'msg': 'geterror'}, status=400)
            publish_dict = serializers.PublishModelSerializer(publish_obj, many=False).data
            return Response({'status': 0, 'msg': 'ok', 'results': publish_dict})
        else:
            publish_query = models.Publish.objects.all()
            publish_obj = serializers.PublishModelSerializer(publish_query,many=True)
            return Response({'status':0,'msg':'ok','results':publish_obj.data})
        

序列化类配置:

# 子序列化类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # fields = '__all__'
        fields = ['name','price']

class PublishModelSerializer(serializers.ModelSerializer):
    #
    books = BookModelSerializer(many=True)
    class Meta:
        model = models.Publish
        fields = ['name','address','books']
        
        
        # 了解配置
        # fields = '__all__'
        # exclude = ['name']
        # depth = 2  # 自动深度,值代表深度次数,但是被深度的外键采用__all__,显示所有字段

实现十大接口

url配置

from django.conf.urls import url, include
from django.conf import settings
from . import views
from . import models
from django.views.static import serve

urlpatterns = [
    url(r'^v1/books/$',views.BookAPIView.as_view()),
    url(r'^v1/books/(?P<pk>\d+)/$',views.BookAPIView.as_view()),
]

models模型类配置

class Book(BaseModel):
    name = models.CharField(max_length=16)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.ForeignKey(to='Publish',
                                related_name='books', db_constraint=False,
                                on_delete=models.DO_NOTHING)
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

    @property
    def publish_info(self):
        return {
            'name':self.publish.name,
            'address':self.publish.address
        }

    @property
    def author_list(self):
        author_list_temp = []
        authors = self.authors.all()
        for author in authors:
            author_dict = {
                'name':author.name
            }
            # 有详情才处理详情信息
            try:
                author_dict['mobile'] = author.detail.mobile
            except:
                author_dict['mobile'] = '无'

            author_list_temp.append(author_dict)
        return author_list_temp

class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)

    @property
    def books_list(self):
        book_list = []
        books = self.books.all()
        for book in books:
            book_dict = {
                'name':book.name,
                'price':book.price
            }
            book_list.append(book_dict)
        return book_list


class Author(BaseModel):
    name = models.CharField(max_length=16)


class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    author = models.OneToOneField(to='Author', related_name='detail',db_constraint=False, on_delete=models.CASCADE)

单查群查

接口使用说明

  • 单查:获取pk序列化
  • 群查:获取所有对象序列化,使用many=True参数
请求方式 请求参数 API链接
单查 GET http://127.0.0.1:8000/api/v1/books/n/
群查 GET http://127.0.0.1:8000/api/v1/books/

视图代码:

    def get(self,response,*args,**kwargs):
        pk = kwargs.get('pk')
        if pk:
            # 单查
            book_obj = models.Book.objects.filter(is_delete=False,pk=pk).first()
            if not book_obj:
                return APIResponse(1,'error',http_status=400)
            book_ser = serializers.BookModelSerializer(book_obj)
        else:
            # 群查
            book_query = models.Book.objects.filter(is_delete=False).all()
            book_ser = serializers.BookModelSerializer(book_query,many=True)
        return APIResponse(results=book_ser.data)

序列化类代码

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # 配置fields 为__all__ 表示序列化所有的字段
        # fields = '__all__'

        fields = ['name','price','publish','authors','publish_info','author_list']
        extra_kwargs = {
            'publish':{
                'write_only':True
            },
            'authors':{
                'write_only':True
            }
        }

单删群删

  • 单删:提供pk删除
  • 群删:提供列表([pk,,,,,,pk])

接口使用说明

请求方式 请求参数 API链接
单删 DELETE http://127.0.0.1:8000/api/v1/books/n/
群删 DELETE [pk,,,,,pkn] http://127.0.0.1:8000/api/v1/books/

视图代码

    def delete(self, request, *args, **kwargs):
        '''
        删除逻辑:修改is_delete字段,修改成功代表删除成功,修改失败代表删除失败
        单删接口:/books/(pk)
        群删接口:/books/
        '''
        pk = kwargs.get('pk')
        if pk:
            # 将单删格式化成群删一条,列表类型
            pks = [pk]
        else:
            # 群删,提供列表中套pk的类型数据
            pks = request.data
        print(pks)
        try:
            rows = models.Book.objects.filter(is_delete=False,pk__in=pks).update(is_delete=True)
        except:
            return APIResponse(1,'数据有误')

        if rows:
            return APIResponse(0,'删除成功')
        return APIResponse(1,'删除失败')

单增群增

  • 单增:提供pk和字典数据,many=False
  • 群增:提供列表套字段数据,many=True

接口使用说明

请求方式 请求参数 API链接
单删 POST {"k":"v"} http://127.0.0.1:8000/api/v1/books/
群删 POST [{"k":"v"},{"k":"v"}] http://127.0.0.1:8000/api/v1/books/

视图代码

    def post(self,request,*args,**kwargs):
        '''
        单增接口:/books/,数据:{...}
        群增接口:/books/,数据:[{...},{...}]
        处理逻辑:将数据交给反序列化类处理,数据的类型关系到many属性是否为True
        '''
        if isinstance(request.data,dict):
            many = False
        elif isinstance(request.data,list):
            many = True
        else:
            return APIResponse(1,'数据有误',http_status=400)
        book_ser = serializers.BookModelSerializer(data=request.data,many=many)
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()
        return APIResponse(results=serializers.BookModelSerializer(book_obj,many=many).data)

反序列化类代码

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # 配置fields 为__all__ 表示序列化所有的字段
        # fields = '__all__'

        fields = ['name','price','publish','authors','publish_info','author_list']
        extra_kwargs = {
            'publish':{
                'write_only':True
            },
            'authors':{
                'write_only':True
            }
        }

整体单改群改

接口使用说明

  • 整体单改要提供主键值和字典数据(需提供全部字段,不需要提供主键)
  • 整体群改要提供一个大列表包含数据字典(需提供全部字段和主键)
  • 整体群改需要重写ListSerializer的update方法
请求方式 请求参数 API链接
整体单改 PUT {"k":"v"} http://127.0.0.1:8000/api/v1/books/n/
整体群改 PUT [{"k":"v"},{"k":"v"}] http://127.0.0.1:8000/api/v1/books/

视图代码

    def put(self, request, *args, **kwargs):
        '''
        单改接口:/books/(pk),数据:{"k":"v"}
        群改接口:/books/,数据:[{"k":"v"},{"k":"v"}]
        逻辑:将数据交给反序列化类处理,数据的类型关系到many属性是否为True
        '''
        pk = kwargs.get('pk')
        if pk:
            # 单改
            try:
                book_obj = models.Book.objects.get(is_delete=False,pk=pk)
            except:
                return APIResponse(1,'pk error',http_status=400)
            book_ser = serializers.BookModelSerializer(instance=book_obj,data=request.data)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
        else:
            # 群改
            # 数据时列表套字典,每个字典中必须带pk,如果有一条没有带pk,返回数据有误
            # 如果pk对应的对象已被删除或不存在,返回数据有误
            try:
                pks = []
                for dic in request.data:
                    pks.append(dic.pop('pk'))
                book_query = models.Book.objects.filter(is_delete=False,pk__in=pks).all()
                if len(pks) != len(book_query):
                    raise Exception('pk对应的数据不存在')
            except Exception as e:
                return Response({'detail':'%s'%e},status=400)

            book_ser = serializers.BookModelSerializer(instance=book_query,data=request.data,many=True)
            # print(book_ser)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj,many=True).data)

序列化类

# 多表操作群改
class BookListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        return super().create(validated_data)

    def update(self, instance_list, validated_data_list):
        return [
            self.child.update(instance_list[index],attrs)  for index,attrs in enumerate(validated_data_list)
        ]

# 子序列化类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # ModelSerializer 配置了ListSerializer辅助类,帮助完成群增和群改
        # 如果只有群增,不需要自定义配置,
        # 如果要完成群改,必须自定义配置
        list_serializer_class = BookListSerializer
        model = models.Book
        # 配置fields 为__all__ 表示序列化所有的字段
        # fields = '__all__'

        fields = ['name','price','publish','authors','publish_info','author_list']
        extra_kwargs = {
            'publish':{
                'write_only':True,
                # 'required':True
            },
            'authors':{
                'write_only':True
            }
        }
    def validate(self, attrs):
        print(self)
        print('.................')
        print('传入的request对象:%s'% self.context.get('request'))
        return attrs

局部单改群改

接口使用说明

  • 局部单改提供主键值和数据字典(字段选填)
  • 局部群改群改要提供一个大列表包含数据字典(字段选填)
  • 设置参数将所有字段变成可选参数partial=True
请求方式 请求参数 API链接
局部单改 PATHC {"k":"v"} http://127.0.0.1:8000/api/v1/books/n/
局部群改 PATHC [{"k":"v"},{"k":"v"}] http://127.0.0.1:8000/api/v1/books/

视图代码

    def patch(self, request, *args, **kwargs):
        '''
        单改接口:/books/(pk),数据:{"k":"v"}
        群改接口:/books/,数据:[{"k":"v"},{"k":"v"}]
        逻辑:将数据交给反序列化类处理,数据的类型关系到many属性是否为True
        '''
        pk = kwargs.get('pk')
        if pk:
            # 单改
            try:
                book_obj = models.Book.objects.get(is_delete=False,pk=pk)
            except:
                return APIResponse(1,'pk error',http_status=400)
            book_ser = serializers.BookModelSerializer(instance=book_obj,data=request.data,partial=True)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
        else:
            # 群改
            # 数据时列表套字典,每个字典中必须带pk,如果有一条没有带pk,返回数据有误
            # 如果pk对应的对象已被删除或不存在,返回数据有误
            try:
                pks = []
                for dic in request.data:
                    pks.append(dic.pop('pk'))
                book_query = models.Book.objects.filter(is_delete=False,pk__in=pks).all()
                if len(pks) != len(book_query):
                    raise Exception('pk对应的数据不存在')
            except Exception as e:
                return Response({'detail':'%s'%e},status=400)

            book_ser = serializers.BookModelSerializer(instance=book_query,data=request.data,many=True,partial=True, context={'request': request})
            # print(book_ser)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj,many=True).data)

序列化类

# 多表操作群改
class BookListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        return super().create(validated_data)

    def update(self, instance_list, validated_data_list):
        return [
            self.child.update(instance_list[index],attrs)  for index,attrs in enumerate(validated_data_list)
        ]

# 子序列化类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # ModelSerializer 配置了ListSerializer辅助类,帮助完成群增和群改
        # 如果只有群增,不需要自定义配置,
        # 如果要完成群改,必须自定义配置
        list_serializer_class = BookListSerializer
        model = models.Book
        # 配置fields 为__all__ 表示序列化所有的字段
        # fields = '__all__'

        fields = ['name','price','publish','authors','publish_info','author_list']
        extra_kwargs = {
            'publish':{
                'write_only':True,
                # 'required':True
            },
            'authors':{
                'write_only':True
            }
        }
    def validate(self, attrs):
        print(self)
        print('.................')
        print('传入的request对象:%s'% self.context.get('request'))
        return attrs
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!