zoukankan      html  css  js  c++  java
  • drf序列化与反序列化,基表的概念

    在序列化与反序列化之前,需要了解的知识点

    模型表知识点

    基表的概念:

      创建的基表其他表可以继承,能继承其中的字段,用于许多张表共有几个字段时,可以使用基表

    基表创建方式:

    class BaseModel(models.Model):
        create_time = models.DateTimeField(auto_now_add=True)
        is_delete = models.BooleanField(default=False)
    
        class Meta:
            # abstract默认为False
            # abstract为True不会创建此表
            abstract = True

    外键字段中的断连接,反向查询与on_delete

    class AuthorDetail(BaseModel):
        adders = models.CharField(max_length=255)
        mobile = models.CharField(max_length=11)
        author = models.OneToOneField(
            to='Author',
            # 反向查询都是通过related_name,默认情况都是表名小写
            # 用原生orm时,反向查询结果为多个时,需要加_set,但是一旦定义related_name,反向查询结果为多个时,也不需要加_set
            related_name='detail',
            # 断关联
            # 减缓数据库压力
            db_constraint=False,
            # on_delete 默认情况下是models.CASCADE
            # DO_NOTHING author记录删除,详情表不做任何修改
            # SET_DEFAULT 作者记录被删除时,author一对一字段会变成你设置的默认值,不过你要提前设置default
            # SET_NULL 作者记录被删除时,author一对一字段会变成空(null) 也要提前设置null=True
            on_delete=models.DO_NOTHING
        )
    
        class Meta:
            db_table = 'day71_author_detail'
            verbose_name = '作者详情'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return '%s的详情' % self.author.name

    注:

      断连情况下,两张表已经没有数据库关系了,再去操作数据库不会级联更新级联删除

      我们加的on_delete=models.CASCADE是让orm通过逻辑进行级联删除

    序列化与反序列化(ModelSerializer)

    序列化

    class Publishserializers(serializers.ModelSerializer):
        class Meta:
            model = models.Publish
            fields = ('name', 'adders')
    
    
    class Bookserializers(serializers.ModelSerializer):
        # 自定义字段必须写在fields中
        # 了解: 该方式设置的序列化字段,必须在fields中声明
        # 自定义连表深度 - 子序列化方式
        # 如果自定义字段与model类中的字段重名时,会走自定义字段
        publish = Publishserializers()
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'publish', 'get_author', 'gender')
    
            # 所有字段
            # fields = '__all__'
            # 与fields不共存,exclude排除哪些字段
            # exclude = ('id', 'is_delete', 'create_time')
            # 自动连表深度
            # depth = 1

    序列化fields中可以添加模型类中序列化插拔属性,

    说的简单点就是,在模型类中定义方法,最好是用property装饰器装饰

    # 序列化插拔式属性 - 完成自定义字段名完成连表查询
        @property
        def get_author(self):
            # print(self.authors, type(self.authors))
            return self.authors.values('name', 'age')

    模型类中的插拔式属性,在序列化中的fields可写可不写

    注:

      自定义字段最好不要与模型表中的字段重名

    反序列化

    class BookDeserializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'publish', 'authors')
            # extra_kwargs为了反序列化时添加系统校验
            extra_kwargs = {
                'name': {
                    'max_length': 10,
                    'min_length': 3,
                    # required默认为True,所以默认为必填字段
                    'required': True,
                    'error_messages': {
                        'max_length': '姓名最长10位',
                        'min_length': '姓名最短3位',
                        'required': '姓名必填'
                    }
                },
            }
        
        # 局部钩子
        def validate_name(self, value):
            if 'g' in value:
                raise exceptions.ValidationError('你这个鸡贼')
            return value
    
        # 全局钩子
        def validate(self, attrs):
            # 外键字段前端传过来的都是id值,但是后端会将其转成对象,
            # 如果传过来的id值,并不在数据库中,会直接报错,如果我们写了raise_exception=True会直接将错返回给前端
            book_obj = models.Book.objects.filter(publish=attrs.get('publish'), name=attrs.get('name'))
            # print(book_obj)
            if book_obj:
                raise exceptions.ValidationError({"name": '书名重复'})
            return attrs

    序列化与反序列化可以合并,上面只是方便演示

    """
    1) fields中设置所有序列化与反序列化字段
    2) extra_kwargs划分只序列化或只反序列化字段
        write_only:只反序列化
        read_only:只序列化
        自定义字段默认只序列化(read_only)
    3) 设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则
    """
    
    
    class V2Bookserializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'img', 'price', 'authors', 'publish', 'publish_name', 'get_author')
            extra_kwargs = {
                'img': {
                    'read_only': True
                },
                'publish_name': {
                    'read_only': True
                },
                'get_author': {
                    'read_only': True
                },
                'name': {
                    'max_length': 10,
                    'min_length': 3,
                    'error_messages': {
                        'max_length': '姓名最长10位',
                        'min_length': '姓名最短3位',
                        'required': '姓名必填'
                    }
                }
            }
    
        def validate_name(self, value):
            if 'g' in value:
                raise exceptions.ValidationError('你这个鸡贼')
            return value
    
        def validate(self, attrs):
            book_obj = models.Book.objects.filter(name=attrs.get('name'), publish=attrs.get('publish'))
            if book_obj:
                raise exceptions.ValidationError({'name': '同一出版社书名不能重复'})
            return attrs

    再次提醒:

      自定义连表深度(自定义属性)默认为read_only只参加序列化

    ##### 视图层:views.py
    
    ```python
    class V2Book(APIView):
        # 单局部改:对 v2/books/(pk)/ 传的数据,数据字段key都是选填
        # 群局部改:对 v2/books/ 
        # 请求数据 - [{pk:1, name:123}, {pk:3, price:7}, {pk:7, publish:2}]
        def patch(self, request, *args, **kwargs):
            request_data = request.data
            pk = kwargs.get('pk')
    
            # 将单改,群改的数据都格式化成 pks=[要需要的对象主键标识] | request_data=[每个要修改的对象对应的修改数据]
            if pk and isinstance(request_data, dict):  # 单改
                pks = [pk, ]
                request_data = [request_data, ]
            elif not pk and isinstance(request_data, list): # 群改
                pks = []
                for dic in request_data:  # 遍历前台数据[{pk:1, name:123}, {pk:3, price:7}, {pk:7, publish:2}],拿一个个字典
                    pk = dic.pop('pk', None)
                    if pk:
                        pks.append(pk)
                    else:
                        return Response({
                            'status': 1,
                            'msg': '数据有误',
                        })
            else:
                return Response({
                    'status': 1,
                    'msg': '数据有误',
                })
    
            # pks与request_data数据筛选,
            # 1)将pks中的没有对应数据的pk与数据已删除的pk移除,request_data对应索引位上的数据也移除
            # 2)将合理的pks转换为 objs
            objs = []
            new_request_data = []
            for index, pk in enumerate(pks):
                try:
                    # pk对应的数据合理,将合理的对象存储
                    obj = models.Book.objects.get(pk=pk)
                    objs.append(obj)
                    # 对应索引的数据就需要保存下来
                    new_request_data.append(request_data[index])
                except:
                    # 重点:反面教程 - pk对应的数据有误,将对应索引的data中request_data中移除
                    # index = pks.index(pk)
                    # request_data.pop(index)
                    continue
    
            book_ser = serializers.V2BookModelSerializer(instance=objs, data=new_request_data, partial=True, many=True)
            book_ser.is_valid(raise_exception=True)
            book_objs = book_ser.save()
    
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.V2BookModelSerializer(book_objs, many=True).data
            })
    ```
     

     

     

     

     

  • 相关阅读:
    很好用的DBHelper类
    .net连接SAP的几种方式
    使用API管理平台的优势
    ASP.NET 2.0中的登陆控件简介
    asp.net 1.1/ 2.0 中快速实现单点登陆
    ASP.NET 2.0 成员资格和角色管理授权
    SqlDataSource 调用带参数的存储过程
    Asp.net中基于Forms验证的角色验证授权
    解释:ASP.NET 2.0 中的窗体身份验证
    SQL JOIN 的用法
  • 原文地址:https://www.cnblogs.com/asdaa/p/11689849.html
Copyright © 2011-2022 走看看