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

http://blog.leanote.com/post/1982916058@qq.com/django-REST-framework%E7%9A%84%E5%90%84%E7%A7%8DView%E8%A7%86%E5%9B%BE%E7%B1%BB

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)
View Code

错误信息格式为 {'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(实战篇)——过滤、搜索、排序、分页

Filtering(过滤器)

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,
        }
View Code

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
View Code

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
View Code

 

 

 

posted @ 2019-08-30 19:38  番茄土豆西红柿  阅读(284)  评论(0编辑  收藏  举报
TOP