zoukankan      html  css  js  c++  java
  • 序列化组件二

    一、Response二次封装

    我们想要对响应类进行自定义,以后就用我们自己自定义的响应类

    # 新建response.py文件
    from rest_framework.response import Response
    
    class APIResponse(Response):
        def __init__(self, status=0, msg='ok', http_status=None, headers=None, exception=False, **kwargs):
            # 将外界传入的数据状态码、状态信息以及其他所有额外存储在kwargs中的信息,都格式化成data数据
            data = {
                'status': status,
                'msg': msg
            }
            # 在外界数据可以用result和results来存储
            if kwargs:
                data.update(kwargs)
    
            super().__init__(data=data, status=http_status, headers=headers, exception=exception)
    
    
    # 使用:
    # APIResponse() 代表就返回 {"status": 0, "msg": "ok"}
    
    # APIResponse(result="结果") 代表返回 {"status": 0, "msg": "ok", "result": "结果"}
    
    # APIResponse(status=1, msg='error', http_status=400, exception=True) 异常返回 {"status": 1, "msg": "error"}

    二、数据库关系

    1、断连分析

    """
    1)之间有关系的两个表,增删改操作会相互影响(效率低),查询操作就是正常的连表操作
    2)之间有关系的两个表,断开关联,但所有数据保持与原来一致
        每个表都可以单独操作,增删改操作效率极高,但是容易出现脏数据(开发中完全可以避免)
        由于数据没有任何变化,所以查询的连表操作不会受到任何影响
        
    3)Django的ORM支持断关联操作关系表,且所有的操作方式和没有断关联操作一致
    """

    2、orm操作关系

    """
    外键位置:
    1)一对多:ForeignKey必须放在多的一方,书与出版社,外键应该放在书表
    2)多对多:ManyToManyField放在任何一方都可以,因为会创建关系表,在关系表中用两个外键分别关联两个表
    3)一对一:OneToOneField放在依赖的表,作者与作者详情,放在详情表,OneToOneField会被转换为 外键 + 唯一约束
    """
    
    """
    ORM关系Field:
    ForeignKey可以设置related_name, db_constraint, on_delete
    OneToOneField可以设置related_name, db_constraint, on_delete
    ManyToManyField只能设置related_name, db_constraint
        不能设置on_delete的原因:不管是关联的A表,还是B表,数据修改,都会影响到关系表(默认级联),
        如果想控制,只能自定义关系表,在关系表的两个外键分别设置on_delete
    """
    
    """
    参数含义
    related_name:表之间反向访问的名字,默认是 表名小写|表名小写_set
    db_constraint:表之间的关联关系,默认为True,代表关联,设置False,可以提高增删改的效率,且不影响查等其他操作
    on_delete:在django 1.x下默认是CASCADE,在django 2.x下必须手动明确
    """
    
    """
    表关系:
    作者没,作者详情一定没:CASCADE  *****
    作者没,书还是该作者出的:DO_NOTHING
    部门没,部门内的员工全部进入未分组部门:SET_DEFAULT (需要配合default属性使用)  
    部门没,部门内的员工部门外键字段设置为空:SET_NULL (需要配合null=True属性使用)  *****
    """

    3、案列

    class Author(models.Model):
        name = models.CharField(max_length=64)
    
    
    class AuthorDetail(models.Model):
        phone = models.CharField(max_length=11)
        # related_name就是可以方向访问,比如主键在作者详情,作者访问作者详情就可以直接.detail就相当于来到了详情表
        # db_constraint是控制两张表之间是否有关联,默认是True,改为False就是断关联
        author = models.OneToOneField(to=Author,
                                      related_name='detail',
                                      db_constraint=False,
                                      # on_delete=models.CASCADE
                                      # on_delete=models.DO_NOTHING
                                      # default=1
                                      on_delete=models.SET_NULL,
                                      null=True
                                      )

    测试

    import os, django  # 导入环境
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "d_proj.settings") # 加载配置文件
    django.setup()  # 启动
    
    from api.models import Author, AuthorDetail
    
    # 测试正向反向查询
    # a1 = Author.objects.first()
    # print(a1.name)
    # print(a1.detail.phone)
    
    # ad2 = AuthorDetail.objects.last()
    # print(ad2.phone)
    # print(ad2.author.name)
    
    # 级联关系测试
    # django1.x版本默认的就是级联删除,级联更新,所以我们把作者删除了,那在作者详情中关联作者的那个字段也会被删除
    # ad2 = AuthorDetail.objects.last()  # type: AuthorDetail
    # ad2.delete()
    
    # Author.objects.first().delete()

    三、基表

    # 基类:是抽象的(不会完成数据库迁移),目的是提供一些共有的字段
    class BaseModelk(models.Model):
        is_delete = models.BooleanField(default=False)
        updated_time = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            abstract = True  # 注意:必须完成该配置才可以让别的类去继承基类

    应用

    class Book(BaseModel):
        name = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
        image = models.ImageField(upload_to='img', default='img/default.png')
    
        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)
    
    
    class Publish(BaseModel):
        name = models.CharField(max_length=64)
    
    
    class Author(BaseModel):
        name = models.CharField(max_length=64)
    
    
    class AuthorDetail(BaseModel):
        phone = models.CharField(max_length=11)
        author = models.OneToOneField(
            to=Author,
            related_name='detail',
            db_constraint=False,
            on_delete=models.SET_NULL,
            null=True
        )

    四、序列化的其他配置(了解)

    1、补充

    class AuthorModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Author
            # 不常用,将全部字段提供给外界
            fields = '__all__' 
            
    # ------------------------------------------------------------------
    
    class AuthorModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Author
            # 不常用,排除指定字段的其他所有字段,不能自动包含 外键反向 字段
            exclude = ['is_delete', 'updated_time']  
            
    # ------------------------------------------------------------------
    
    class AuthorModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Author
            # 'detail', 'books' 是 外键(正向|反向) 字段
            fields = ['name', 'detail', 'books']
            # 不常用,自动深度,自动深度会显示外键关联表的所有字段
            depth = 2  
    # 正向外键字段:就是外键的属性名
    # 反向外键字段:就是外键属性设置的related_name

    2、子序列化

    """
    1)子序列化的字段,必须是 外键(正向|反向) 字段
    2)子序列化 一定有上下关系.:子序列化类必须写在上方,且只能对外键(正向反向)字段进行覆盖,一般只有查需求的资源,可以采用子序列化
    3)子序列化对应的数据是单个many=False,数据对应是多个many=True
    4)子序列化其实就是自定义序列化字段,覆盖了原有 外键(正向|反向)字段 的规则,所以不能进行反序列化
    """

    3、案列

    子路由urls.py

    url(r'^authors/$', views.AuthorAPIView.as_view()),
    url(r'^authors/(?P<pk>d+)/$', views.AuthorAPIView.as_view()),

    serializers.py

    from rest_framework import serializers
    from . import models
    class AuthorDetailModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.AuthorDetail
            fields = ['phone']
    
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ['name', 'price']
    
    class AuthorModelSerializer(serializers.ModelSerializer):
        # 子序列化:子序列化类必须写在上方,且只能对 外键(正向反向)字段 进行覆盖
        # 注:运用了子序列化的外键字段,就不能进行数据库的反序列化过程
        detail = AuthorDetailModelSerializer(many=False, read_only=True)
        books = BookModelSerializer(many=True, read_only=True)
        # 问题:
        # 1)不设置read_only时,就相当于允许反序列化,反序列化时就会报错
        # 2)设置read_only时,可以完成反序列化,但是新增的数据再序列化了,就没有外键关联的数据,与原来数据格式就不一致了
        class Meta:
            model = models.Author
            fields = ['name', 'detail', 'books']

    views.py

    # 实际开发,资源的大量操作都是查询操作,只有查需求的资源,可以采用子序列化
    class AuthorAPIView(APIView):
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                obj = models.Author.objects.filter(is_delete=False, pk=pk).first()
                serializer = serializers.AuthorModelSerializer(instance=obj)
                return APIResponse(result=serializer.data)
            else:
                queryset = models.Author.objects.filter(is_delete=False).all()
                serializer = serializers.AuthorModelSerializer(instance=queryset, many=True)
                return APIResponse(results=serializer.data)
    
        # 测试子序列化外键字段,不能参与反序列化
        def post(self, request, *args, **kwargs):
            serializer = serializers.AuthorModelSerializer(data=request.data)
            if serializer.is_valid():
                obj = serializer.save()
                return APIResponse(result=serializers.AuthorModelSerializer(instance=obj).data, http_status=201)
            else:
                # 校验失败 => 异常响应
                return APIResponse(1, serializer.errors, http_status=400)

    五、多表序列化与反序列化

    """
    1)外键字段要参与反序列化,所以外键字段设置为write_only
    2)外键关系需要连表序列化结果给前台,可以用@property来自定义连表序列化
    """

    案例

    urls.py

    url(r'^books/$', views.BookAPIView.as_view()),
    url(r'^books/(?P<pk>d+)/$', views.BookAPIView.as_view()),

    models.py

    class Book(BaseModel):
        name = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
        image = models.ImageField(upload_to='img', default='img/default.png')
    
        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  # @property字段默认就是read_only,且不允许修改
        def publish_name(self):
            return self.publish.name
    
        @property  # 自定义序列化过程
        def author_list(self):
            temp_author_list = []
            for author in self.authors.all():
                author_dic = {
                    "name": author.name
                }
                try:
                    author_dic['phone'] = author.detail.phone
                except:
                    author_dic['phone'] = ''
                temp_author_list.append(author_dic)
            return temp_author_list
    
        @property  # 借助序列化类完成序列化过程
        def read_author_list(self):
            from .serializers import AuthorModelSerializer
            return AuthorModelSerializer(self.authors.all(), many=True).data
    
    
    class Publish(BaseModel):
        name = models.CharField(max_length=64)
    
    
    class Author(BaseModel):
        name = models.CharField(max_length=64)
    
    
    class AuthorDetail(BaseModel):
        phone = models.CharField(max_length=11)
        author = models.OneToOneField(
            to=Author,
            related_name='detail',
            db_constraint=False,
            on_delete=models.SET_NULL,
            null=True
        )

    serializers.py

    # 辅助序列化格式化的类
    class AuthorDetailModelSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.AuthorDetail
            fields = ['phone']
    
    
    # 辅助序列化格式化的类
    class AuthorModelSerializer(serializers.ModelSerializer):
    
        # 使用了子序列化
        detail = AuthorDetailModelSerializer(many=False, read_only=True)  # 一对一关系
    
        class Meta:
            model = models.Author
            fields = ['name', 'detail']
    
    
    # 主序列化类
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # fields = ['name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list', 'read_author_list']
            fields = ['name', 'price', 'image', 'publish', 'authors']
            extra_kwargs = {
                'image': {
                    'read_only': True,
                },
                'publish': {  # 系统原有的外键字段要留给反序列化过程使用,序列化外键内容用@property自定义
                    'write_only': True,
                },
                'authors': {
                    'write_only': True,
                },
            }

    views.py

    # 六个必备接口:单查,群查,单增,单删,单整体改(了解),单局部改
    # 四个额外接口:群增,群删,群整体改,群局部改
    class BOOKAPIView(APIView):
        # 序列化
        # 单查群查
        def get(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if pk:
                obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
                serializer = serializers.BookModelSerializer(instance=obj)
                return APIResponse(result=serializer.data)
    
            else:
                queryset = models.Book.objects.filter(is_delete=False).all()
                serializer = serializers.BookModelSerializer(instance=queryset, many=True)
                return APIResponse(results=serializer.data)
    
    
        # 反序列化
        # 单增群增
        def post(self, request, *args, **kwargs):
            # 如何区别单增群增:request.data是{}还是[]
            if not isinstance(request.data, list):  # isinstance()是用来判断一个对象是否是某个类型
                # 单增
                serializer = serializers.BookModelSerializer(data=request.data)
                serializer.is_valid(raise_exception=True)  # 如果校验失败会直接抛异常返回给前台
                obj = serializer.save()
                # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等
                return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201)
            else:
                # 群增
                serializer = serializers.BookModelSerializer(data=request.data, many=True)
                serializer.is_valid(raise_exception=True)  # 如果校验失败会直接抛异常返回给前台
                objs = serializer.save()
                # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等
                return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)
           # 友情注释:群增其实是借助了ListSerializer来的create方法完成的

    六、小结

    """
    1)二次封装Response:
        自定义类继承Response,重写init方法,在内部格式化data
        
    2)表关系分析:
        断关联:
            优点:提示增删改操作效率,不允许查效率
            缺点:增删改操作可能会导致脏数据,所以需要通过逻辑或是事务来保证
            
    3)ORM表关系处理语法:
        1)外键所在位置
        2)如何断关联db_constraint
        3)正向方向自定义名字:related_name
        4)表关系:on_delete四种
        
    4)基表:Meta中配置abstract=True,来被继承,提供共有字段
    
    5)多表连表Meta中的了解配置
        fields = '__all__'  # 不常用,将全部字段提供给外键
        exclude = ['is_delete', 'updated_time']  # 不常用,排除指定字段的其他所有字段
        depth = 2  # 不常用,主动深度,自动深度会显示关联表的所有字段
        
    6)子序列化
        i)通常只用于序列化过程,对外键字段进行了覆盖,影响外键字段的反序列化过程
        class SubSerializer:
            pass
        class SupSerializer:
            外键 = SubSerializer(many=True|False)
        
    7)多表的序列化与反序列化
        1)连表序列化用自定义@property完成:内部实现可以自定义逻辑,也可以走序列化类
        2)外键字段留给反序列化来使用
        
    8)单查、群查、单增、群增接口
    """

     

  • 相关阅读:
    JavaScript 把函数作为参数进行传值
    面向微服务的企业云计算架构转型
    bzoj1009 GT考试 (kmp+矩阵优化dp)
    noiac64 sort (二分答案)
    luogu1983 车站分级 (拓扑排序)
    loj6157 A ^ BProblem (并查集)
    bzoj1831 逆序对 (dp+树状数组)
    luogu2282/bzoj1219 历史年份 (dp+hash+二分+线段树)
    bzoj3702/bzoj2212 二叉树 (线段树合并)
    cf1073G Yet Another LCP Problem (SA+权值线段树)
  • 原文地址:https://www.cnblogs.com/yafeng666/p/12337908.html
Copyright © 2011-2022 走看看