001 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方法django

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)

顯示以下api

[
    {
        "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

顯示以下markdown

[
    {
        "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

顯示以下:post

[
    {
            "idc": {
                "id": 1,
                "name": "亞太機房"
            },
            "name": "A座6樓601 13排2機櫃"
        }
]

六 對子表機櫃反序列化寫入

反序列化的過程ui

前端提交數據 --> 數據存儲在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)

序列化顯式以下url

[
    {
        "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.

模型rest

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 對應上面的方法三序列化的反序列化

序列化顯式以下,看起來和方法一的顯示層級同樣code

[
    {
        "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)

相關文章
相關標籤/搜索