一 安装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)