zoukankan      html  css  js  c++  java
  • django restful framework 一对多方向更新数据库

    django restful framework 序列化

    案例: 一个网域domain可以绑定多台服务器主机assets, 但是一台服务器只能绑定一个网域. 数据模型之间关系适用于一对多.

    一 . 数据模型: models

    1. 定义 assets 模型:apps/assets/models/asset.py

      #!/usr/bin/env python
      # -*- coding: utf-8 -*-
      
      import uuid
      from django.db import models
      
      class Asset(models.Model):
      
          id = models.UUIDField(default=uuid.uuid4, primary_key=True)
          ops_id = models.CharField(max_length=30, unique=True, null=True, blank=True, verbose_name=_('ops asset id'))
          ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
          hostname = models.CharField(max_length=128, verbose_name=_('Hostname'))
          domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) # 使用Foreignkey关联外键
          created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
          date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
          comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
      
          def __str__(self):
              return '{0.hostname}({0.ip})'.format(self)
      
          class Meta:
              verbose_name = _("Asset")
      
    2. domain 模型如下:apps/assets/models/domain.py

    # -*- coding: utf-8 -*-
    
    import uuid
    
    from django.db import models
    from django.utils.translation import ugettext_lazy as _
    
    class Domain(models.Model):
        id = models.UUIDField(default=uuid.uuid4, primary_key=True)
        name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
        comment = models.TextField(blank=True, verbose_name=_('Comment'))
        date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created'))
    
        class Meta:
            verbose_name = _("Domain")
    
        def __str__(self):
            return self.name
    
    1. 模型说明
    • Assets 模型中有个domain字段,使用 ForeignKey 关联 Domain模型, 并指定 related_name= assets, 表示在domain模型中,会隐藏一个

      assets字段. 在使用反向查找时(通过domain查assets)使用此字段

    二. 序列化: serializers

    1. 序列化类用来对request/response参数进行校验. 这里使用 ModelSerializer

    2. 代码: apps/assets/serializers/domain.py

      # -*- coding: utf-8 -*-
      
      from rest_framework import serializers
      from ..models import Domain
      
      class DomainBindAssetSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Domain
              fields = ['id','name','assets']
              read_only_fields = ('id', 'name')
      
      
          def update(self, instance, validated_data):
              instance.id = validated_data.get('id',instance.id)
              instance.name = validated_data.get('name',instance.name)
              instance.save()
              instance.assets.set(validated_data.get('assets',instance.assets.all()))
              return instance
      
    3. 代码解析

      • fields = ['id','name','assets'] 说明此序列化检验字段
      • update 方法为了绑定 接口 更新字段, 特别注意, assets字段是隐藏字段,不能直接更新domain的assets字段, 需要使用domain.assets.set(object)
      • 最后返回domain实例

    三, 视图: views

    视图函数使用标准的 restful接口.

    1. 实现反向更新domain下的assets

    2. 代码: apps/assets/api/domain.py

      # ~*~ coding: utf-8 ~*~
      
      from common.permissions import IsOrgAdminOrAppUser
      from common.utils import get_logger
      from rest_framework import status
      from rest_framework.generics import RetrieveUpdateDestroyAPIView
      from rest_framework.views import Response
      
      from .. import serializers
      from ..models import Domain, Gateway
      
      class DomainWithAssetsUpdateApi(RetrieveUpdateDestroyAPIView):
          queryset = Domain.objects.all()
          serializer_class = serializers.DomainBindAssetSerializer
          permission_classes = ()
          authentication_classes = ()
      
          def get_object(self, pk):
              try:
                  return Domain.objects.get(id=pk)
              except Domain.DoesNotExist:
                  logger.error("domain id is not existed.")
                  False
      
          def get(self, request, *args, **kwargs):
              """query domain with assets"""
              data = {"msg": '', 'result': None, 'code': None}
              domain = self.get_object(kwargs.get('pk'))
              try:
                  if not domain:
                      raise Exception("Domain not exists! Check url!")
                  serializer = serializers.DomainBindAssetSerializer(domain)
                  code = status.HTTP_200_OK
                  data['result'] = serializer.data
                  data['code'] = code
                  logger.info("Domain bind assets:{}".format(domain.assets))
              except Exception as e:
                  code = status.HTTP_422_UNPROCESSABLE_ENTITY
                  data['msg'] = str(e)
                  data['code'] = code
                  logger.error(str(e))
              finally:
                  return Response(data=data, status=code)
      
          def put(self, request, *args, **kwargs):
              """bind assets to domain"""
              data = {"msg": '', 'result': None, 'code': None}
      
              domain = self.get_object(kwargs.get("pk", None))
              if not domain:
                  code = status.HTTP_404_NOT_FOUND
                  data['msg'] = "Domain not exists, check url!"
                  data['code'] = code
                  return Response(data=data, status=code)
      
              try:
                  serializer = serializers.DomainBindAssetSerializer(data=request.data, instance=domain, partial=True)
                  if serializer.is_valid():
                      serializer.save()
                      code = status.HTTP_202_ACCEPTED
                      data['result'] = serializer.data
                      data['code'] = code
                  else:
                      code = status.HTTP_422_UNPROCESSABLE_ENTITY
                      data['msg'] = serializer.errors
                      data['code'] = code
              except Exception as e:
                  code = 500
                  data['msg'] = str(e)
                  data['code'] = code
                  logger.error("Assets bind domain occur error:{}".format(str(e)))
              finally:
                  return Response(data=data, status=code)
      
    3. 代码说明

      • 接口只实现: 查询(get), 更新(put)接口
      • get接口通过url解析当前查询domain.id, 使用DomainBindAssetSerializer反序列化查询的结果并返回给接口.
      • put接口相对复杂一下:
        • 首先将使用DomainBindAssetSerializer将 请求的字段request.data进行序列化, partial=True表示允许只更新要修改的字段.
        • 将解析的结果进行校验
        • 如果正常,使用save(),即调用 DomainBindAssetSerializer . update()的方法对数据进行更新

    四, 路由: urls

    定义访问路由

    1. 代码如下:apps/assets/urls/api_urls.py

      # coding:utf-8
      from django.urls import path
      from rest_framework_bulk.routes import BulkRouter
      from rest_framework_nested import routers
      
      
      from .. import api
      
      app_name = 'assets'
      
      router = BulkRouter()
      router.register(r'assets', api.AssetViewSet, 'asset')
      router.register(r'domain', api.DomainViewSet, 'domain')
      
      
      urlpatterns = [
      	...
          path('domain/<uuid:pk>/assets/',api.DomainWithAssetsUpdateApi.as_view(), name='domain-assets-update'),
          ...
      ]
      
      urlpatterns += router.urls + cmd_filter_router.urls
      

    五. 测试 : test

    测试使用postman

    1. get 查询接口: /api/assets/v1/domain/e5d52f79-42fc-4147-8c76-296bb7cae37b/assets/
    // 返回格式如下
    {
      "msg": "",
      "result": {
        "id": "e5d52f79-42fc-4147-8c76-296bb7cae37b",
        "name": "mao",
        "assets": [
          "323fff34-1baf-46b8-9784-cb2fc6046966",
          "5c65c106-1750-47de-a2f3-031c07996eda",
          "940cd754-267a-4531-88cd-e4cc248cc936"
        ]
      },
      "code": 200
    }
    
    1. put 更新接口: /api/assets/v1/domain/e5d52f79-42fc-4147-8c76-296bb7cae37b/assets/
    // request data
    {
        "assets": [
          "323fff34-1baf-46b8-9784-cb2fc6046966",
          "5c65c106-1750-47de-a2f3-031c07996eda",
        ]
    }
    
    //response data
    {
      "msg": "",
      "result": {
        "id": "e5d52f79-42fc-4147-8c76-296bb7cae37b",
        "name": "mao",
        "assets": [
          "323fff34-1baf-46b8-9784-cb2fc6046966",
          "5c65c106-1750-47de-a2f3-031c07996eda"
        ]
      },
      "code": 202
    }
    
  • 相关阅读:
    随便瞎说
    第六次作业
    第五次作业
    第四次作业
    第二次作业
    第一周java心得
    Ext.Net学习笔记之动态加载TabPanel
    Ext.net学习笔记之ViewPort布局
    MyEclipse连接mySql数据库笔记
    配置服务器
  • 原文地址:https://www.cnblogs.com/failymao/p/11996910.html
Copyright © 2011-2022 走看看