22-自己学习的DRF笔记
目录
0.学习链接
HyperLinkedModelSerializer
HyperLinkedModelSerializer 是一个值得推荐的 Serializer,它能够自动为 HTMLRenderer 提供相关外键资源的超链接,便于 web 调试。
参考链接
https://q1mi.github.io/Django-REST-framework-documentation/tutorial/quickstart_zh/
https://www.cnblogs.com/liwenzhou/p/9403382.html
https://www.jianshu.com/p/fad41a00c2de
https://juejin.im/post/5a98ee2a51882555677df987
官网:https://www.django-rest-framework.org/api-guide/views/
https://blog.windrunner.me/python/web/django-rest-framework.html
https://cloud.tencent.com/developer/article/1466174
https://segmentfault.com/a/1190000011488275
https://www.qingtingip.com/h_190913.html
https://www.520mwx.com/view/30160
https://blog.csdn.net/aaronthon/article/details/82923029
django rest framework serializers小结
django rest framework mixins小结
django rest framework apiview、viewset总结分析
http://code4fs.xyz/category/3/
Django REST framework的各种技巧——1.基础讲解
Django REST framework的各种技巧——2.serializer
Django REST framework的各种技巧——3.权限
Django REST framework的各种技巧——4.Generic View
Django REST framework的各种技巧——5.搜索
Django REST framework的各种技巧——6.异常处理
Django REST framework的各种技巧——7.导入导出
1.django多认证类
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning", 'ALLOWED_VERSIONS': ['v1', 'v2'], "DEFAULT_VERSION": 'v1', "DEFAULT_AUTHENTICATION_CLASSES": [ 'user.utils.auth.token_auth.LoginTokenAuthtication', 'user.utils.auth.token_auth.ApiTokenAuthtication', ] }
此处DEFAULT_AUTHENTICATION_CLASSES是支持多个认证类,当第一个认证类返回None的时候,自动进行下一个认证类,如果返回了元祖或者异常,则不再尝试后面的认证类,参考:https://www.django-rest-framework.org/api-guide/authentication/
如果是继承BaseAuthentication的话,必须重写authenticate方法,返回值则参考上面说的,如果是继承其他的认证类,比如_TokenAuthentication,这个类authenticate默认下面红色字体返回的是None,即默认本身认证不通过,需要尝试下一个认证流程,但是在实际项目中,如果这个类是最后一个认证的类,这块就得触发异常,否则会导致的现象是认证不通过,却可以访问资源。
class ApiTokenAuthtication(_TokenAuthentication): model = ApiToken def authenticate(self, request): auth = get_authorization_header(request).split() print(auth) if not auth or auth[0].lower() != self.keyword.lower().encode(): raise exceptions.AuthenticationFailed('认证信息有误') if len(auth) == 1: msg = _('Invalid token header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = _('Invalid token header. Token string should not contain spaces.') raise exceptions.AuthenticationFailed(msg) try: token = auth[1].decode() except UnicodeError: msg = _('Invalid token header. Token string should not contain invalid characters.') raise exceptions.AuthenticationFailed(msg) return self.authenticate_credentials(token)
另外
def get_authorization_header(request): """ Return request's 'Authorization:' header, as a bytestring. Hide some test client ickyness where the header can be unicode. """ auth = request.META.get('HTTP_AUTHORIZATION', b'') if isinstance(auth, text_type): # Work around django test client oddness auth = auth.encode(HTTP_HEADER_ENCODING) return auth
由于这块的定义,所以header中传入参数的时候key必须是authorization,可以是大写或者小写,http会自动全部转化为大写,postman示例如下
补充:使用'user.utils.auth.token_auth.ApiTokenAuthtication', 这种认证方式需要手工创建token,创建方式如下,执行python manage.py shell
>>> from user.models import LoginToken, ApiToken, UserInfo >>> UserInfo.objects.get(id=4) <UserInfo: wuxiaoyu> >>> user=UserInfo.objects.get(id=4) >>> ApiToken.objects.get_or_create(user=user) (<ApiToken: 4cde9916b91ae132efdd5c105b418958879ea140>, True) >>>
备注,djaogo默认的表中的token值是用key这个字段存储的,但是在使用select查询的时候,由于key是mysql的关键词会冲突报语法错误,所以解决方式是把key改成其他字符串比如token
原先
from rest_framework.authtoken.models import Token as _Token
class ApiToken(_Token): """ API用户的token信息和指定资源类型分配的操作权限 """ user = models.ForeignKey( settings.AUTH_USER_MODEL, db_constraint=False, on_delete=models.CASCADE, related_name='api_token', verbose_name='User', ) grant_resource = models.CharField(max_length=64, verbose_name='授权的资源类型') grant_operation = models.CharField(max_length=64, verbose_name='授权的操作')
继承的_Token内使用的是key这个单词作为字段名
优化,需要重写部分父类的方法
把父类from rest_framework.authtoken.models import Token 中的所有字段和方法copy过来,去掉key字段,加上token,如果继承的话会有两个主键的冲突!!!
import binascii
import os
class ApiToken(models.Model):
"""
API用户的token信息和指定资源类型分配的操作权限
"""
token = models.CharField(max_length=40, primary_key=True)
user = models.OneToOneField(
settings.AUTH_USER_MODEL, related_name='auth_token',
on_delete=models.CASCADE, verbose_name="User"
)
grant_resource = models.CharField(max_length=64, verbose_name='授权的资源类型')
grant_operation = models.CharField(max_length=64, verbose_name='授权的操作')
created = models.DateTimeField(auto_now_add=True)
class Meta:
# abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS # 这个要注释掉,否则为True的话是抽象类,则不在数据库中创建对应的数据表了
verbose_name = "Token"
verbose_name_plural = "Tokens"
def save(self, *args, **kwargs):
if not self.token:
self.token = self.generate_key()
return super(ApiToken, self).save(*args, **kwargs)
def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()
def __str__(self):
return self.token
同时要重写authenticate_credentials方法,将get函数的key替换为token,如下
class ApiTokenAuthtication(_TokenAuthentication): # 分配的token不失效,除非删除 model = ApiToken def authenticate_credentials(self, key): model = self.get_model() try: token = model.objects.select_related('user').get(token=key) except model.DoesNotExist: raise exceptions.AuthenticationFailed('Invalid token.') if not token.user.is_active: raise exceptions.AuthenticationFailed('User inactive or deleted.') return (token.user, token) def authenticate(self, request): # 重写父类的方法 auth = get_authorization_header(request).split() if not auth or auth[0].lower() != self.keyword.lower().encode(): raise exceptions.AuthenticationFailed('认证信息有误') if len(auth) == 1: msg = 'Invalid token header. No credentials provided.' raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = 'Invalid token header. Token string should not contain spaces.' raise exceptions.AuthenticationFailed(msg) try: token = auth[1].decode() except UnicodeError: msg = 'Invalid token header. Token string should not contain invalid characters.' raise exceptions.AuthenticationFailed(msg) return self.authenticate_credentials(token)
2.django序列化之PrimaryKeyRelatedField
models.py中 class Snippet(models.Model): owner = models.ForeignKey('auth.User', related_name="snippets", on_delete=models.CASCADE) serializers.py当中 from django.contrib.auth.models import User from snippets.models import Snippet class UserSerializer(serializers.ModelSerializer): snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) class Meta: model = User fields = ("id", "username", "snippets") """ 因为'snippets' 在用户模型中是一个反向关联关系。 在使用 ModelSerializer 类时它默认不会被包含, 所以我们需要为它添加一个显式字段。 """
更多可参考:https://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield
3.序列化一对多的创建
https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers 这里介绍的是一对多的关系,但创建的时候是创建【一】这一边的对象,以及如何关联处理【多】 这边的对象,但是如果是反过来,我们是要创建【多】这边的对象,并和【一】这边的对象关联,这个该怎么做呢,
https://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield 这个也是创建【一】这一边的对象,以及如何关联处理【多】 这边的对象,但是
When using SlugRelatedField as a read-write field, you will normally want to ensure that the slug field corresponds to a model field with unique=True.
一对多创建方法如下
class BusinessLineList(generics.ListCreateAPIView): def perform_create(self, serializer): """ 执行perform_create的时候就是is_valid已经通过了 和上面一样,这块实现外键的插入逻辑,有两种思路: 1.插入对象,本例子就是插入队形 2.插入 字段名_id = id """ dp_entry = Department.objects.get(id=self.request.data.get('department_id')) serializer.save(department=dp_entry) queryset = BusinessLine.objects.all() serializer_class = BusinessLineSerializer class BusinessLineDetail(generics.RetrieveUpdateDestroyAPIView): queryset = BusinessLine.objects.all() serializer_class = BusinessLineSerializer
多对多创建方法如下
class HostList(generics.ListCreateAPIView): def perform_create(self, serializer): """ 执行perform_create的时候就是is_valid已经通过了 和上面一样,这块实现外键的插入逻辑,有两种思路: 1.插入对象,本例子就是插入队形 2.插入 字段名_id = id """ host_entry = serializer.save() service_list = self.request.data.get('Service') for service in service_list: sv_entry = Service.objects.get(id=service['id']) host_entry.Service.add(sv_entry) queryset = Host.objects.all() serializer_class = HostSerializer
如果在序列化中实现可参考https://www.jianshu.com/p/62088c9b05d5
4.反序列化 is_valid不通过怎么办
接口序列化的时候默认展示的是models中显示的字段,比如Service的model是
class Service(models.Model): service_name = models.CharField(max_length=64, verbose_name='服务名称') service_env = models.CharField(max_length=64, default='', verbose_name='环境') service_introduce = models.TextField(default='', verbose_name='服务介绍') service_owner = models.CharField(max_length=64, verbose_name='服务负责人') service_qa = models.CharField(max_length=64, verbose_name='服务负责人') service_op = models.CharField(max_length=64, verbose_name='服务负责人') business_line = models.ForeignKey(BusinessLine, on_delete=models.DO_NOTHING) department = models.ForeignKey(Department, on_delete=models.DO_NOTHING) node_key = models.CharField(default='', max_length=128, verbose_name='唯一key') git_registry = models.CharField(default='', max_length=128, verbose_name='服务的git仓库地址') create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') modify_time = models.DateTimeField(auto_now=True, verbose_name='修改时间')
展示的信息是
[ { "id": 1, "service_name": "ops_temp_service", "service_env": "prod", "service_introduce": "ops_temp_service", "service_owner": "[\"jump\"]", "service_qa": "[\"reader\"]", "service_op": "[\"reader\"]", "node_key": "ops_temp_node.ops_temp_bl.ops_temp_service.prod", "git_registry": "git", "create_time": "2019-08-27T10:24:47.674545", "modify_time": "2019-08-27T10:24:47.674568", "business_line": 1, "department": 1 }, ...... ]
如果在serializers.py中没有重写上面的business_line 和 department 字段(下面的例子展示了通过SerializerMethodField的方式重写的department字段),则在反序列化(即创建)的时候必须也传入business_line 和 department 字段,否则serializer.is_valid(),会验证不通过,可以去如下源码打印错误信息
def is_valid(self, raise_exception=False): assert not hasattr(self, 'restore_object'), ( 'Serializer `%s.%s` has old-style version 2 `.restore_object()` ' 'that is no longer compatible with REST framework 3. ' 'Use the new-style `.create()` and `.update()` methods instead.' % (self.__class__.__module__, self.__class__.__name__) ) assert hasattr(self, 'initial_data'), ( 'Cannot call `.is_valid()` as no `data=` keyword argument was ' 'passed when instantiating the serializer instance.' ) if not hasattr(self, '_validated_data'): try: self._validated_data = self.run_validation(self.initial_data) except ValidationError as exc: self._validated_data = {} self._errors = exc.detail else: self._errors = {} if self._errors and raise_exception: raise ValidationError(self.errors) print('----') print(self._errors) print(bool(self._errors)) return not bool(self._errors)
错误信息格式为 {'department': [ErrorDetail(string='This field is required.', code='required')]} 但是在serializers.py中重写了字段为什么就不要求这个required了呢?因为使用如下这种方式序列化,没有指定required=True
class ServiceSerializer(serializers.ModelSerializer): department = serializers.SerializerMethodField(read_only=True) class Meta: model = Service fields = '__all__' def get_department(self, obj): return { 'id': obj.department.id, 'department_name': obj.department.department_name }
备注:ServiceSerializer 在 __init__中自动设置了read-only=True,但是read_only和required属性是不可以同时设置的,设置了之后就会报错
5.Filtering(过滤器)
查看源码过程
from django_filters.rest_framework import DjangoFilterBackend
查看DjangoFilterBackend的get_filterset这个方法,从这块开始查询源码
这个默认支持的是字段名__exact=过滤值,即精确匹配的方式
实现指定字段过滤的方法
from django_filters.rest_framework import DjangoFilterBackend class HostList(CmdbPermissionControl, generics.ListCreateAPIView):
queryset = Host.objects.all()
serializer_class = HostSerializer
# 过滤
filter_backends = (DjangoFilterBackend,)
filter_fields = ('InstanceId',) # 这块支持多个字段
自定义filte可以参考:https://q1mi.github.io/Django-REST-framework-documentation/api-guide/filtering_zh/
6.Search(搜索)
实现指定字段搜索的方法
from rest_framework.filters import SearchFilter class HostList(CmdbPermissionControl, generics.ListCreateAPIView):
queryset = Host.objects.all()
serializer_class = HostSerializer
# 搜索
filter_backends = (SearchFilter,)
# 只要这些字段包含serarch等于的值,则返回这条记录,也可以改造成URL传递要搜索哪个字段
search_fields = ('InstanceId', 'env', 'id')
search fileds使用like做的,所以存在效率问题,如果有并发什么的需求,请接入其他搜索
可以动态的指定search fileds,参考:https://www.django-rest-framework.org/api-guide/filtering/#searchfilter,示例
class CustomSearchFilter(SearchFilter):
def get_search_fields(self, view, request):
print('000000000000')
if request.query_params.get('service_owner'):
return ['service_owner']
return super(CustomSearchFilter, self).get_search_fields(view, request)
class ServiceList(CmdbPermissionControl, generics.ListCreateAPIView):
filter_backends = (CustomSearchFilter,)
# 过滤
# search_fields = ('service_owner',)
queryset = Service.objects.all()
serializer_class = ServiceSerializer
请求示例:http://localhost:8000/api/v1/cmdb/service?search=xiaoyutest&service_owner=true
如果报错:UnorderedObjectListWarning: Pagination may yield inconsistent results with an unorder,
参考:https://stackoverflow.com/questions/44033670/python-django-rest-framework-unorderedobjectlistwarning ,解决方法重写父类的get_queryset方法,将queryset = queryset.all()变为queryset = queryset.all().order_by('id'),完成代码如下
class ServiceList(CmdbPermissionControl, generics.ListCreateAPIView): # 搜索 filter_backends = (CustomSearchFilter,) def get_queryset(self): """ 重写父类的此方法,修改如下: queryset = queryset.all() 变为 queryset = queryset.all().order_by('id') """ assert self.queryset is not None, ( "'%s' should either include a `queryset` attribute, " "or override the `get_queryset()` method." % self.__class__.__name__ ) queryset = self.queryset if isinstance(queryset, QuerySet): queryset = queryset.all().order_by('id') # 修改在这里 return queryset 其他代码......
search的字段可以使用双下划线跨表,参考官方文档:https://www.django-rest-framework.org/api-guide/filtering/#searchfilter
You can also perform a related lookup on a ForeignKey or ManyToManyField with the lookup API double-underscore notation:
search_fields = ['username', 'email', 'profile__profession']
7.ordering(排序)
可以使用ordering_fields属性明确指定可以对哪些字段执行排序(这有助于防止意外的数据泄露,例如允许用户对密码散列字段或其他敏感数据进行排序)。
如果不指定ordering_fields属性,则默认为可以对serializer_class属性指定的串行器上的任何可读字段进行过滤。
使用ordering属性设置默认排序:ordering = ('username',)
代码示例
from rest_framework.filters import OrderingFilter
class UserListView(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserSerializer filter_backends = (OrderingFilter,) ordering_fields = ('username', 'email')
8.分页()
9.过滤搜索排序分页可组合操作
参考:
django rest framework 数据的查找、过滤、排序
Django REST framework的各种技巧——5.搜索 ---可以自定义filter class
django-rest-framework(实战篇)——过滤、搜索、排序、分页
10.django rest framework 向数据库中插入数据时处理外键的方法
一.models.py中
from django.db import models class UserModel(models.Model) user_name = models.CharField() class MyModel(models.Model) author = models.Foreignkey(user) age = models.CharField()
二. 序列化文件 serializers.py 中创建序列化类
from rest_framework.serializers import Serializer from models import MyModel class MySerializer( Serializer.ModelSerializers): # 自定义字段 user_name = serializers.SerializerMethodField() class Meta: model = MyModel fields = ("user_name", "age") # 处理自定义的字段返回用户名, 通过外键获取UserModel的数据 def get_user_name(self, obj): return obj.author.user_name def create(self, validated_data)
# 处理外键字段 return MyModel.objects.create(author=self.context["author"], **validated_data)
三.视图文件views.py中定义视图
# 视图函数中 from rest_framework.views import APIView # 类视图中使用,集成自该类 from rest_framework.decorators import api_view # 方法视图中使用,是一个装饰器,直接装饰方法视图 from rest_framework import status from rest_framework.response import Response # 直接可以将字典数据转换成json数据 from models import MyModel from serializers import MySerializer from rest_framework.request import Request from rest_framework import exceptions @api_view(['GET', 'POST', 'PUT', 'DELETE']) # 指明允许那些请求方式进行请求 def api_list(request): if request.method == 'GET': # 向数据库查询数据,得到查询集 try: query_set = MyModel.objects.all() except Snippet.DoesNotExist: return Response(status=status.HTTP_417_EXPECTATION_FAILED) # 调用序列化类对象,返回进行了序列化的字段集合,使用serializers.data方式 获取数据 serializers = MySerializer(query_set, many=True) # 取出数据,向客户端进行返回,Response()会将数据转化为json数据. return Response(serializers.data, status=status.HTTP_200_OK) # 前端提交数据,调用模型,将其保存到数据库中 elif request.method == 'POST':
user = request.user #将外键数据插入 这里插入一个user对象,就是外键所对应的主表的一个对象 serializer = MySerializer(data= request.data, context={"author":user}) # 如果反序列化的对象存在,就说明数据有效,将数据保存到数据库中 if serializer.is_valid(): # 调用save(), 从而调用序列化对象的create()方法,创建一条数据 serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
11.Django RestFramework 自定义字段返回
在接口返回数据时,如果数据库表中查询出来的某些字段为null时,在前端需要多处理一些数据异常的情况。
django可以自定义序列化返回处理,将返回的内容限制和预处理再返回到前端。
1.未处理时返回
如图上,有email、mobile这两个字段是有可以为空且默认值为null的。
2.to_representation处理
在模型序列化类增加, to_representation方法,以自定义数据处理限制
from rest_framework import serializers from .models import UserInfo class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = UserInfo # fields = '__all__' fields = ( 'id', 'email', 'date_create', 'mobile', 'email', 'notice_voice', 'notice_email', 'notice_sms', 'notice_push') def to_representation(self, instance): data = super().to_representation(instance) if not data['email']: data['email'] = "" if not data['mobile']: data['mobile'] = "" return data
12.自定义权限控制类
参考:官方文档
Note: when you set new permission classes through class attribute or decorators you're telling the view to ignore the default list set over the settings.py file.
如果要实现RBAC,可以结合casbin实现
import casbin from rest_framework.permissions import BasePermission from django.core.exceptions import PermissionDenied class RbacPolicy(BasePermission): def __init__(self): """ 本意是只加载一次, 当policy修改后通过reload方法重新加载,但目前实现是每次请求都加载一次,待优化 """ self.enforcer = casbin.Enforcer("RBAC/authz_model.conf", "RBAC/authz_policy.csv") def has_permission(self, request, view): user = request.user.username if request.user.is_anonymous: user = 'anonymous' path = request.path method = request.method permission_check = self.enforcer.enforce(user, path, method) if not permission_check: self.require_permission() else: return permission_check def require_permission(self,): raise PermissionDenied
13.DRF request相关数据
request.data
request.query_params
request.parsers
request.user
request.auth
request.authenticators
request.methon
request.content_type
等等,参考requests官方文档
实践
1.部门业务线服务主机基于通用视图的增删改查
models.py
from django.db import models class Department(models.Model): department_name = models.CharField(max_length=64, verbose_name='部门名称') department_introduce = models.TextField(default='',verbose_name='部门介绍') department_owner = models.CharField(max_length=64, verbose_name='部门业务负责人') department_qa = models.CharField(max_length=64, verbose_name='部门测试负责人') department_op = models.CharField(max_length=64, verbose_name='部门业务运维负责人') node_key = models.CharField(default='', max_length=128, verbose_name='唯一key') create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') modify_time = models.DateTimeField(auto_now=True, verbose_name='修改时间') def serialize(self): return { 'department_name': self.department_name, 'department_introduce': self.department_introduce, 'department_owner': self.department_owner, 'department_qa': self.department_qa, 'department_op': self.department_op } class BusinessLine(models.Model): bl_name = models.CharField(max_length=64, verbose_name='业务线名称') bl_introduce = models.TextField(default='', verbose_name='业务线介绍') bl_owner = models.CharField(max_length=64, verbose_name='业务线负责人') bl_qa = models.CharField(max_length=64, verbose_name='业务线测试负责人') bl_op = models.CharField(max_length=64, verbose_name='业务线业务运维负责人') department = models.ForeignKey(Department, related_name='department', on_delete=models.DO_NOTHING) node_key = models.CharField(default='', max_length=128, verbose_name='唯一key') create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') modify_time = models.DateTimeField(auto_now=True, verbose_name='修改时间') def serialize(self): return { 'bl_name': self.bl_name, 'bl_introduce': self.bl_introduce, 'bl_owner': self.bl_owner, 'bl_qa': self.bl_qa, 'bl_op': self.bl_op, 'department': self.department, } class Service(models.Model): service_name = models.CharField(max_length=64, verbose_name='服务名称') service_env = models.CharField(max_length=64, default='', verbose_name='环境') service_introduce = models.TextField(default='', verbose_name='服务介绍') service_owner = models.CharField(max_length=64, verbose_name='服务负责人') service_qa = models.CharField(max_length=64, verbose_name='服务负责人') service_op = models.CharField(max_length=64, verbose_name='服务负责人') business_line = models.ForeignKey(BusinessLine, on_delete=models.DO_NOTHING) department = models.ForeignKey(Department, on_delete=models.DO_NOTHING) node_key = models.CharField(default='', max_length=128, verbose_name='唯一key') git_registry = models.CharField(default='', max_length=128, verbose_name='服务的git仓库地址') create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') modify_time = models.DateTimeField(auto_now=True, verbose_name='修改时间') def serialize(self): return { 'service_name': self.service_name, 'service_introduce': self.service_introduce, 'service_owner': self.service_owner, 'service_qa': self.service_qa, 'service_op': self.service_op, 'business_line': self.business_line, 'department': self.department } class Host(models.Model): env = models.CharField(max_length=64, default='', verbose_name='机器所属环境') InstanceId = models.CharField(max_length=64, default='', verbose_name='InstanceId') InstanceName = models.CharField(max_length=64, default='', verbose_name='InstanceName') RegionId = models.CharField(max_length=64, default='', verbose_name='RegionId') LocalName = models.CharField(max_length=64, default='', verbose_name='LocalName') ZoneId = models.CharField(max_length=64, default='', verbose_name='ZoneId') Description = models.TextField(default='', verbose_name='Description') Status = models.CharField(max_length=64, default='', verbose_name='Status') HostName = models.CharField(max_length=64, default='', verbose_name='HostName主机名') PublicIpAddress = models.CharField(max_length=1024, default='', verbose_name='外网ip列表') PrivateIpAddress = models.CharField(max_length=1024, default='', verbose_name='内网ip列表') Cpu = models.CharField(max_length=64, default='', verbose_name='Cpu') Memory = models.CharField(max_length=64, default='', verbose_name='Memory') OSType = models.CharField(max_length=64, default='', verbose_name='OSType') OSName = models.CharField(max_length=64, default='', verbose_name='OSName') InstanceNetworkType = models.CharField(max_length=64, default='', verbose_name='InstanceNetworkType') CreationTime = models.DateTimeField(max_length=64, default='1970-01-01 00:00', verbose_name='CreationTime') StartTime = models.DateTimeField(max_length=64, default='1970-01-01 00:00', verbose_name='StartTime') ExpiredTime = models.DateTimeField(max_length=64, default='1970-01-01 00:00', verbose_name='ExpiredTime') Tags = models.CharField(max_length=256, default='', null=True, verbose_name='Tags') InternetChargeType = models.CharField(max_length=64, default='', verbose_name='InternetChargeType') InstanceChargeType = models.CharField(max_length=64, default='', verbose_name='InstanceChargeType') Service = models.ManyToManyField(Service) def serialize(self, field=[]): if len(field) > 0: pass else: return { 'PublicIpAddress': self.PublicIpAddress, 'PrivateIpAddress': self.PrivateIpAddress, 'Cpu': self.Cpu, 'Memory': self.Memory, 'OSType': self.OSType, 'OSName': self.OSName, 'HostName': self.HostName, 'Description': self.Description, 'Status': self.Status, # 'Service': self.Service, 'Tags': self.Tags, 'InstanceId': self.InstanceId, 'InstanceName': self.InstanceName, 'RegionId': self.RegionId, 'LocalName': self.LocalName, 'ZoneId': self.ZoneId, 'CreationTime': self.CreationTime, 'StartTime': self.StartTime, 'ExpiredTime': self.ExpiredTime, 'InstanceNetworkType': self.InstanceNetworkType, 'InternetChargeType': self.InternetChargeType, 'InstanceChargeType': self.InstanceChargeType, }
serializers.py
#! /usr/bin/env python # -*- coding: utf-8 -*- from rest_framework import serializers from .models import Department, BusinessLine, Service, Host class DepartmentSerializer(serializers.ModelSerializer): class Meta: model = Department fields = '__all__' class BusinessLineSerializer(serializers.ModelSerializer): department = serializers.SerializerMethodField(read_only=True) class Meta: model = BusinessLine fields = '__all__' def get_department(self, obj): return { 'id': obj.department.id, 'department_name': obj.department.department_name } class ServiceSerializer(serializers.ModelSerializer): department = serializers.SerializerMethodField(read_only=True) business_line = serializers.SerializerMethodField(read_only=True) class Meta: model = Service fields = '__all__' def get_department(self, obj): return { 'id': obj.department.id, 'department_name': obj.department.department_name } def get_business_line(self, obj): return { 'id': obj.business_line.id, 'department_name': obj.business_line.bl_name } class HostSerializer(serializers.ModelSerializer): Description = serializers.CharField(allow_blank=True) Service = serializers.SerializerMethodField() class Meta: model = Host fields = '__all__' def get_Service(self, obj): result = [] for service in obj.Service.all(): result.append({ 'id': service.id, 'service_name': service.service_name }) return result
api_view.py
#! /usr/bin/env python # -*- coding: utf-8 -*- from cmdb.models import Department, BusinessLine, Service, Host from cmdb.serializers import DepartmentSerializer, BusinessLineSerializer, ServiceSerializer, HostSerializer from rest_framework import generics from rest_framework import status from rest_framework.response import Response # 没有外键和manytomany的单独表 # 1.使用mixins混合类实现 + 自己实现视图 # class DepartmentList(mixins.ListModelMixin, # mixins.CreateModelMixin, # generics.GenericAPIView): # queryset = Department.objects.all() # serializer_class = DepartmentSerializer # # def get(self, request, *args, **kwargs): # return self.list(request, *args, **kwargs) # # def post(self, request, *args, **kwargs): # return self.create(request, *args, **kwargs) # # # class DepartmentDetail(mixins.RetrieveModelMixin, # mixins.UpdateModelMixin, # mixins.DestroyModelMixin, # generics.GenericAPIView): # queryset = Department.objects.all() # serializer_class = DepartmentSerializer # # def get(self, request, *args, **kwargs): # return self.retrieve(request, *args, **kwargs) # # def put(self, request, *args, **kwargs): # return self.update(request, *args, **kwargs) # # def delete(self, request, *args, **kwargs): # return self.destroy(request, *args, **kwargs) # 2.通用视图 = 使用mixins混合类实现 + django的自动完成视图 class DepartmentList(generics.ListCreateAPIView): queryset = Department.objects.all() serializer_class = DepartmentSerializer class DepartmentDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Department.objects.all() serializer_class = DepartmentSerializer # 有Foreignkey的表 # 1.使用mixins混合类实现 + 自己实现视图 # class BusinessLineList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): # queryset = BusinessLine.objects.all() # serializer_class = BusinessLineSerializer # # def perform_create(self, serializer): # """ # 这块实现外键的插入逻辑,有两种思路: # 1.插入对象,本例子就是插入队形 # 2.插入 字段名_id = id # """ # dp_entry = Department.objects.get(id=self.request.data.get('department_id')) # serializer.save(department=dp_entry) # # def get(self, request, *args, **kwargs): # return self.list(request, *args, **kwargs) # # def post(self, request, *args, **kwargs): # return self.create(request, *args, **kwargs) # # # class BusinessLineDetail(mixins.RetrieveModelMixin, # mixins.UpdateModelMixin, # mixins.DestroyModelMixin, # generics.GenericAPIView): # queryset = BusinessLine.objects.all() # serializer_class = BusinessLineSerializer # # def get(self, request, *args, **kwargs): # return self.retrieve(request, *args, **kwargs) # # def put(self, request, *args, **kwargs): # return self.update(request, *args, **kwargs) # # def delete(self, request, *args, **kwargs): # return self.destroy(request, *args, **kwargs) # 2.通用视图 = 使用mixins混合类实现 + django的自动完成视图 class BusinessLineList(generics.ListCreateAPIView): def perform_create(self, serializer): """ 执行perform_create的时候就是is_valid已经通过了 和上面一样,这块实现外键的插入逻辑,有两种思路: 1.插入对象,本例子就是插入队形 2.插入 字段名_id = id """ dp_entry = Department.objects.get(id=self.request.data.get('department_id')) serializer.save(department=dp_entry) queryset = BusinessLine.objects.all() serializer_class = BusinessLineSerializer class BusinessLineDetail(generics.RetrieveUpdateDestroyAPIView): queryset = BusinessLine.objects.all() serializer_class = BusinessLineSerializer # 有两个Foreignkey的表(和上面一个的逻辑类似) # 1.通用视图 = 使用mixins混合类实现 + django的自动完成视图(这个例子没有尝试自己写视图的代码) class ServiceList(generics.ListCreateAPIView): def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer, kwargs['env']) headers = self.get_success_headers(serializer.data) return serializer.data, headers def perform_create(self, serializer, env): """ 执行perform_create的时候就是is_valid已经通过了 和上面一样,这块实现外键的插入逻辑,有两种思路: 1.插入对象,本例子就是插入队形 2.插入 字段名_id = id """ dp_entry = Department.objects.get(id=self.request.data.get('department_id')) bl_entry = BusinessLine.objects.get(id=self.request.data.get('business_line_id')) node_key = dp_entry.department_name + '.' + bl_entry.bl_name + '.' + self.request.data.get('service_name') + '.' + env serializer.save(department=dp_entry, business_line=bl_entry, node_key=node_key, service_env=env) def post(self, request, *args, **kwargs): # 自定义post方法,实现一个serializer 反序列化多次 serializer_all_env_data = [] for env in ['prod', 'stag', 'test', 'dev']: data, headers = self.create(request, env=env, *args, **kwargs) serializer_all_env_data.append(data) return Response(serializer_all_env_data, status=status.HTTP_201_CREATED, headers=headers) queryset = Service.objects.all() serializer_class = ServiceSerializer class ServiceDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Service.objects.all() serializer_class = ServiceSerializer # 有两个ManyToManyField的表(和上面一个的逻辑类似) class HostList(generics.ListCreateAPIView): def perform_create(self, serializer): """ 执行perform_create的时候就是is_valid已经通过了 和上面一样,这块实现外键的插入逻辑,有两种思路: 1.插入对象,本例子就是插入队形 2.插入 字段名_id = id """ host_entry = serializer.save() service_list = self.request.data.get('Service') for service in service_list: sv_entry = Service.objects.get(id=service['id']) host_entry.Service.add(sv_entry) queryset = Host.objects.all() serializer_class = HostSerializer class HostDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Host.objects.all() serializer_class = HostSerializer