• 01.drf文档及外键字段反序列化


    一 安装drf

    • 1.1 安装库
    pip install djangorestframework
    pip install markdown       # Markdown support for the browsable API.
    pip install django-filter  # Filtering support
    
    • 1.2 settings 添加配置
        'rest_framework',
    

    二 接口文档

    • 2.1 安装库
    pip3 install coreapi
    
    • 2.2 settings 添加配置
    REST_FRAMEWORK = {
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    }
    
    • 2.3 添加url

    url(r'^docs/', include_docs_urls("运维平台接口文档"))

    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
        path('docs/', include_docs_urls(title="运维平台API接口文档",description="django drf 接口文档")),
    ]
    

    三 单个表CRUD

    • 3.1 models
    class Idc(models.Model):
        name    = models.CharField("机房名称",max_length=32)
        address = models.CharField("机房地址",max_length=256)
        phone   = models.CharField("联系人",max_length=15)
        email   = models.EmailField("邮件地址",default="null")
        letter  = models.CharField("IDC简称",max_length=5)
    
        def __str__(self):
            return self.name
    
        class Meta:
            db_table = 'resources_idc'
    
    • 3.2 serializers

    serializers.Serializer不需要指定模型,需要序列化那些字段都需要显式指定,同时需要重写createupdate方法

    class IdcSerializer(serializers.Serializer):
        """
        Idc 序列化类
        """
    
        id = serializers.IntegerField(read_only=True)
        name = serializers.CharField(required=True, max_length=32, label="机房名称", help_text="机房名称",  error_messages={"blank": "机房名称不能为空", "required": "这个字段为必要字段"})
        address = serializers.CharField(required=True, max_length=256, label="机房地址", help_text="IDC详细地址", error_messages={"blank": "这个字段不能为空", "required": "这个字段为必要字段"} )
        phone = serializers.CharField(required=True, max_length=15, label="联系电话", help_text="联系电话")
        email = serializers.EmailField(required=True, label="email", help_text="email地址")
        letter = serializers.CharField(required=True, max_length=5, label="字母简称", help_text="字母简称")
    
        def create(self, validated_data):
            return Idc.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            instance.name = validated_data.get("name", instance.name)
            instance.address = validated_data.get("address", instance.address)
            instance.phone = validated_data.get("phone", instance.phone)
            instance.email = validated_data.get("email", instance.email)
            instance.save()
            return instance
    
    
    • 3.3 views
    class IdcViewset(viewsets.ModelViewSet):
        queryset = Idc.objects.all()
        serializer_class = IdcSerializer
    
    • 3.4 路由
    from django.urls import path,include
    from rest_framework.routers import DefaultRouter
    from idcs.views import IdcViewset
    
    router = DefaultRouter()
    router.register('idcs',IdcViewset,basename='idcs')
    urlpatterns = [
        path('', include(router.urls))
        ]
    
    • 3.5 此时可以完成单个模型的CRUD操作
    [
        {
            "id": 1,
            "name": "亚太机房",
            "address": "神舟路999号",
            "phone": "13812345678",
            "email": "xxx@com.cn",
            "letter": "ytjf"
        },
        {
            "id": 2,
            "name": "亚太机房1",
            "address": "神舟路999号1",
            "phone": "13812345678",
            "email": "xxx@com.cn",
            "letter": "ytjf1"
        },
        {
            "id": 3,
            "name": "亚太机房2",
            "address": "神舟路999号2",
            "phone": "13812345678",
            "email": "xxx@com.cn",
            "letter": "ytjf2"
        }
    ]
    

    四 增加一对多中的多,在多的模型中指定外键字段

    • 4.1 models
    
    class Cabinet(models.Model):
        idc = models.ForeignKey(Idc, verbose_name="所在机房",on_delete=models.CASCADE)
        name = models.CharField(max_length=255)
    
        def __str__(self):
            return self.name
    
        class Meta:
            db_table = "resources_cabinet"
            ordering = ["id"]
    
    • 4.2 serializers
    class CabinetSerializer(serializers.Serializer):
        idc = serializers.PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all())
        name = serializers.CharField(required=True)
    
    • 4.3 views
    class CabinetViewset(viewsets.ModelViewSet):
        queryset = Cabinet.objects.all()
        serializer_class = CabinetSerializer
    
    • 4.4 如果不重写create方法,提交时报错,但可以正常显示
    # get
    [
        {
            "idc": 1,
            "name": "A座6楼 13排2机柜"
        },
        {
            "idc": 1,
            "name": "A座1楼 13排2机柜"
        },
        {
            "idc": 1,
            "name": "A座2楼 13排2机柜"
        }
    ]
    # post
    Exception Value: `create()` must be implemented.
    

    五 显示给前端时,应该显示idc的名称,而不是id

    5.1 方法一,使用1对n中的1序列化
    from idcs.serializers import IdcSerializer
    class CabinetSerializer(serializers.Serializer):
        idc = IdcSerializer(many=False)
        name = serializers.CharField(required=True)
    

    显示如下

    [
        {
            "idc": {
                "id": 1,
                "name": "亚太机房",
                "address": "神舟路999号",
                "phone": "13812345678",
                "email": "xxx@com.cn",
                "letter": "ytjf"
            },
            "name": "A座6楼601 13排2机柜"
        }
    ]
    
    5.2 方法二,使用 SerializerMethodField
    from idcs.serializers import IdcSerializer
    class CabinetSerializer(serializers.Serializer):
        idc_name = serializers.SerializerMethodField()
        name = serializers.CharField(required=True)
    
        def get_idc_name(self,obj):
            return obj.idc.name
    

    显示如下

    [
        {
            "idc_name": "亚太机房",
            "name": "A座6楼601 13排2机柜"
        }
    ]
    
    5.3 方法三,使用 PrimaryKeyRelatedField

    to_representation中进行额外字段的增加

    class CabinetSerializer(serializers.Serializer):
        idc = serializers. PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all())
        name = serializers.CharField(required=True)
        
        def to_representation(self, instance):
            idc_obj = instance.idc
            ret = super(CabinetSerializer, self).to_representation(instance)
            ret["idc"] = {
                "id": idc_obj.id,
                "name": idc_obj.name
            }
            return ret
    

    显示如下:

    [
        {
                "idc": {
                    "id": 1,
                    "name": "亚太机房"
                },
                "name": "A座6楼601 13排2机柜"
            }
    ]
    

    六 对子表机柜反序列化写入

    反序列化的过程

    前端提交数据 --> 数据存储在request的get、post、body中 --> drf通过to_internal_value获取原始的数据 
    --> 进入单独字段级别的验证 validated_字段 --> 全部字段验证(比如重复数据 validated)--> 进入表级别的验证
    
    6.1 对应上面的方法一序列化的反序列化
    from idcs.serializers import IdcSerializer
    class CabinetSerializer(serializers.Serializer):
        idc = IdcSerializer(many=False)
        name = serializers.CharField(required=True)
    # 重写 create 方法
        def create(self, validated_data):
            return Cabinet.objects.create(**validated_data)
    

    序列化显式如下

    [
        {
            "idc": {
                "id": 1,
                "name": "亚太机房",
                "address": "神舟路999号",
                "phone": "13812345678",
                "email": "xxx@com.cn",
                "letter": "ytjf"
            },
            "name": "A座6楼601 13排2机柜"
        },
    
    • 6.1.1 创建一个机柜,post如下值
    {
        "idc": 1,
        "name": "A座3楼 13排2机柜"
    }
    
    • 6.1.2 报错如下,因为序列化的时候 idc 是一个字典
    or key, value in self.value.items():
    AttributeError: 'int' object has no attribute 'items'
    
    • 6.1.3 传入字典
    {
        "idc": {
                    "id": 1,
                    "name": "亚太机房",
                    "address": "神舟路999号",
                    "phone": "13812345678",
                    "email": "xxx@com.cn",
                    "letter": "ytjf"
                },
        "name": "A座3楼601 13排2机柜"
    }
    
    • 6.1.4 报错如下,因为机柜的idc是一个外键字段,django的orm需要传入对应外键模型的实例
    "Cabinet.idc" must be a "Idc" instance.
    

    模型

    class Cabinet(models.Model):
        idc = models.ForeignKey(Idc, verbose_name="所在机房",on_delete=models.CASCADE)
        name = models.CharField(max_length=255)
    
    6.2 解决方法
    • 6.2.1 因为是在ORM层报的错,因此可以在 vaildate 中进行机房实例的获取
    def validate(self, attrs):
        idc_data = attrs.pop('idc')
        idc_name = idc_data['name']
        try:
            idc_inst = Idc.objects.get(name=idc_name)
            attrs['idc'] = idc_inst
            return attrs
        except Idc.DoesNotExist:
            raise serializers.ValidationError('机房%s不存在'%idc_name)
    
    • 6.2.2 返回数据如下
    {
        "idc": {
            "id": 1,
            "name": "亚太机房",
            "address": "神舟路999号",
            "phone": "13812345678",
            "email": "xxx@com.cn",
            "letter": "ytjf"
        },
        "name": "A座3楼601 13排2机柜"
    }
    
    • 6.2.3 为什么不用id查找?
      打印一下前端传过来的 id 字段
    for k,v in idc_data.items():
        print(k,v)
        # 因为idc的模型 id 字段在序列化时是只读,反序列化时会被丢弃
        # OrderedDict([('idc', OrderedDict([('name', '亚太机房'), ('address', '神舟路999号'), ('phone', '13812345678'), ('email', 'xxx@com.cn'), ('letter', 'ytjf')])), ('name', 'A座3楼601 13排2机柜')])
    
    • 6.2.4 传入的字段必须满足字段验证
    {
        "idc": {
            "name": "亚太机房"
        },
        "name": "A座4楼601 13排2机柜"
    }
    
    {
        "idc": {
            "address": [
                "这个字段为必要字段"
            ],
            "phone": [
                "This field is required."
            ],
            "email": [
                "This field is required."
            ],
            "letter": [
                "This field is required."
            ]
        }
    }
    
    6.3 对应上面的方法二序列化的反序列化
    • 6.3.1 序列化显式如下
    [
        {
            "idc_name": "亚太机房",
            "name": "A座6楼601 13排2机柜"
        }
    ]
    
    • 6.3.2 接口文档要求字段如下,如果外键字段不允许为 null,则没有办法进行外键字段的反序列化
      image
    6.4 对应上面的方法三序列化的反序列化

    序列化显式如下,看起来和方法一的显示层级一样

    [
        {
            "idc": {
                "id": 1,
                "name": "亚太机房"
            },
            "name": "A座6楼601 13排2机柜"
        }
    ]
    
    • 6.4.1 前端传入数据
    {
        "idc": 1,
        "name": "A座4楼601 13排2机柜"
    }
    
    • 6.4.2 返回如下
    {
        "idc": {
            "id": 1,
            "name": "亚太机房"
        },
        "name": "A座4楼601 13排2机柜"
    }
    

    七序列化和反序列化总结

    • 7.1 models
    # 父表
    class Idc(models.Model):
        name    = models.CharField("机房名称",max_length=32)
        address = models.CharField("机房地址",max_length=256)
        phone   = models.CharField("联系人",max_length=15)
        email   = models.EmailField("邮件地址",default="null")
        letter  = models.CharField("IDC简称",max_length=5)
    
        def __str__(self):
            return self.name
    
        class Meta:
            db_table = 'resources_idc'
    # 子表
    class Cabinet(models.Model):
        idc = models.ForeignKey(Idc, verbose_name="所在机房",on_delete=models.CASCADE)
        name = models.CharField(max_length=255)
    
        def __str__(self):
            return self.name
    
        class Meta:
            db_table = "resources_cabinet"
            ordering = ["id"]
    
    • 7.2 子表的serializers
    class CabinetSerializer(serializers.Serializer):
        idc = serializers.PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all())
        name = serializers.CharField(required=True)
    
    
        def to_representation(self, instance):
            idc_obj = instance.idc
            ret = super(CabinetSerializer, self).to_representation(instance)
            ret["idc"] = {
                "id": idc_obj.id,
                "name": idc_obj.name
            }
            return ret
        def create(self, validated_data):
            return Cabinet.objects.create(**validated_data)
    

    八 总结

    • 方法1 idc = IdcSerializer(many=False)指定父级序列化时,指定需要显示的字段,效果和方法3一样,反序列化时需要使用在 validate 进行外键字段的实例获取(捕获不存在时可修改为增加)
    • 方法2 def get_字段名(self,obj)显示的字段可以与当前序列化的字段在同一个级别,如果需要显示多个字段,代码量稍微会多一点,外键字段不允许为null,则无法进行反序列化
    • 方法3 PrimaryKeyRelatedField 不管是 1:n n:1 n:n 都可以进行控制,在 to_representation 进行外键字段的序列化显式,反序列化时前端只需要正常传入外键的id值即可,推荐使用该方法

    所有的字段在序列化时可以更改显示名字children = serializers.IntegerField(source='parent_group',required=False)

  • 相关阅读:
    算法学习记录单链表
    算法学习记录排序
    算法学习记录图——最小生成树之prim算法
    算法学习记录图——应用之拓扑排序(Topological Sort)
    算法学习记录图(DFS BFS)
    算法学习记录排序——冒泡排序(Bubble Sort)
    算法学习记录图——最小路径之Floyd算法
    自用SqlHelper.cs
    WPF的DataGrid的标题加粗样式
    .NET Framework 4.0不能用sqlite的问题解决方法
  • 原文地址:https://www.cnblogs.com/jenvid/p/12907320.html
Copyright © 2020-2023  润新知