django restful framework一対の多方向更新データベース

9357 ワード

目次
  • django restful frameworkシーケンス化
  • 1.データモデル:models
  • 2.シーケンス化:serializers
  • 3、ビュー:views
  • 4、ルーティング:urls
  • 5.テスト:test

  • django restful frameworkシーケンス化


    ケース:1つのネットワークドメインdomainは、複数のサーバホストassetsをバインドすることができるが、1つのサーバは1つのネットワークドメインしかバインドできない.データモデル間の関係は一対多に適用する.

    一.データモデル:models

  • 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")
    
  • 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
    
  • モデル説明
  • Assetsモデルにはdomainのフィールドがあり、ForeignKeyを使用してDomainのモデルを関連付け、domainモデルではrelated_name= assetsのフィールドが非表示になることを示すassetsを指定する.逆検索を使用する場合(domainでassetsを調べる)このフィールド
  • を使用します.

    二.シーケンス化:serializers

  • シーケンス化クラスはrequest/responseパラメータを検証するために用いる.ここではModelSerializer
  • を用いる
  • コード: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
    
  • コード解析
  • fields = ['id','name','assets'] は、このシーケンス化検査フィールド
  • を示す.
  • updateの方法インタフェース をバインドするため、特にassetsフィールドは非表示フィールドであり、domainのassetsフィールドを直接更新することはできず、domainを使用する必要がある.assets.set(object)
  • は、domainインスタンス
  • を最後に返す.

    三、ビュー:views


    ビュー関数は標準のrestfulインタフェースを使用する.
  • 逆更新domainでのassets
  • を実現
  • コード: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)
    
  • コード説明
  • インタフェースは、クエリー(get)、更新(put)インタフェース
  • のみを実装する.
  • getインタフェースはurlを通じて現在のクエリーdomainを解析する.idは、DomainBindAssetSerializerを用いるクエリの結果を逆シーケンス化し、インタフェースに返す.
  • putインタフェースは比較的複雑です.
  • は、まず、要求するフィールドDomainBindAssetSerializerrequest.dataでシーケンス化し、 partial=Trueは、変更するフィールドのみの更新を許可することを示す.
  • 解析結果を検証する
  • が正常である場合、save()が使用する、DomainBindAssetSerializerが呼び出される.update()の方法でデータを更新する
  • .


    四、ルーティング:urls


    アクセスルートの定義
  • コードは、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//assets/',api.DomainWithAssetsUpdateApi.as_view(), name='domain-assets-update'),
        ...
    ]
    
    urlpatterns += router.urls + cmd_filter_router.urls
    
  • です.

    五.テスト:test


    テストpostmanの使用
  • 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
    }
    
  • 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
    }