相关文章推荐
千杯不醉的甘蔗  ·  Get Started with ...·  1 月前    · 
光明磊落的蚂蚁  ·  orleans/build.ps1 at ...·  5 月前    · 
买醉的石榴  ·  ON DUPLICATE KEY ...·  10 月前    · 
果断的土豆  ·  Windows OpenGL ...·  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": "[email protected]", "letter": "ytjf" "id": 2, "name": "亚太机房1", "address": "神舟路999号1", "phone": "13812345678", "email": "[email protected]", "letter": "ytjf1" "id": 3, "name": "亚太机房2", "address": "神舟路999号2", "phone": "13812345678", "email": "[email protected]", "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": "[email protected]",
                "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": "[email protected]", "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": "[email protected]", "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']
            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": "[email protected]", "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', '[email protected]'), ('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)