目錄
django restful framework 序列化
案例: 一個網域domain可以綁定多台服務器主機assets
, 但是一台服務器只能綁定一個網域. 數據模型之間關系適用於一對多.
一 . 數據模型: 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
模型, 並指定related_name= assets
, 表示在domain模型中,會隱藏一個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接口相對復雜一下:
- 首先將使用
DomainBindAssetSerializer
將 請求的字段request.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/<uuid:pk>/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
}