一 安裝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
不需要指定模型,需要序列化那些字段都需要顯式指定,同時需要重寫create
和update
方法
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,則沒有辦法進行外鍵字段的反序列化
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)