Django 序列化& api文档 & 分页 & 搜索 & drf权限管理

Django 序列化与api文档的例子

创建项目 
django-admin startproject devops



创建一个 app  
python manage.py startapp idcs

 
 
创建一个apps的文件夹
在apps 目录下创建 __init__.py 文件

以后创建app,都需要将创建的项目都移动至该目录中



在项目settings.py里面添加
说明: sys.path.insert 这行要在添加在 BASE_DIR 行的后面
import sys
sys.path.insert(0,os.path.join(BASE_DIR,"apps"))



在项目settings.py文件 INSTALLED_APPS 添加
    'rest_framework',
    'idcs.apps.IdcsConfig',



在项目settings.py里面添加
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'devops',        
        'USER': 'root',           
        'PASSWORD': 'chengce243',       
        'HOST': '127.0.0.1',     
        'PORT': '3306',                   
    }
}



在 apps/idcs 目录下的 __init__.py 文件添加如下内容
import pymysql
pymysql.version_info = (1, 4, 6, 'final', 0)  # change mysqlclient version
pymysql.install_as_MySQLdb()



安装
pip3 install djangorestframework
pip3 install markdown  
pip3 install django-filter 



主路由
from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^',include('idcs.urls'))
]



项目路由
from django.conf.urls import url, include
from . import views
from rest_framework.routers import  DefaultRouter

route = DefaultRouter()
route.register("idcs", views.IdcViewset)
urlpatterns = [
    url(r'^', include(route.urls))
]


创建idcs/models.py
from django.db import models

class Idc(models.Model):
    name = models.CharField("IDC名称",max_length=128,blank=False,null=True)
    address = models.CharField("IDC地址",max_length=200,default="")
    phone = models.CharField("IDC联系电话",max_length=20,null=True)
    email = models.EmailField("IDC邮箱")

    def __str__(self):
        return self.name

    class Meta:
        db_table = "idc"


        
创建idcs/serializers.py
from rest_framework import serializers
from .models import Idc
from django.utils.translation import ugettext_lazy as _

class IdcSerializer(serializers.Serializer):
    """
    Idc 序列化类
    """
    id          = serializers.IntegerField(read_only=True)
    name        = serializers.CharField(required=True, max_length=32,
                                        label="机房名称",
                                        help_text="机房名称",
                                        error_messages={"blank":"机房名称不能为空",
                                                        "required": "这个字段为必要字段"})
    address     = serializers.CharField(required=True,
                                        max_length=256,
                                        label="机房地址",
                                        help_text="IDC详细地址",
                                        error_messages={"blank": "这个字段不能为空",
                                                        "required": "这个字段为必要字段"}
                                        )
    phone       = serializers.CharField(required=True, max_length=15, label="联系电话", help_text="联系电话")
    email       = serializers.EmailField(required=True, label="email", help_text="email地址")

    def create(self, validated_data):
        return Idc.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.name = validated_data.get("name", instance.name)
        instance.address = validated_data.get("address", instance.address)
        instance.phone = validated_data.get("phone", instance.phone)
        instance.email = validated_data.get("email", instance.email)
        instance.save()
        return instance
    
    
创建idcs/views.py
from rest_framework import viewsets
from .models import Idc
from .serializers import IdcSerializer

class IdcViewset(viewsets.ModelViewSet):
    """
    retrieve:
        返回指定Idc信息
    list:
        返回IDC列表
    update:
        更新IDC信息
    destroy:
        删除IDC记录
    create:
        创建IDC记录
    partial_update:
        更新部分字段
    """
    queryset = Idc.objects.all()
    serializer_class = IdcSerializer
   
   
   
数据库同步  
python manage.py makemigrations
python manage.py migrate



生成数据 
python manage.py shell


In [3]: from idcs.models import Idc

In [4]: idc = Idc()

In [5]: idc.name = "徐汇机房"

In [6]: idc.address = "上海市徐汇区"

In [7]: idc.phone = "1234567890"

In [8]: idc.email = "xuhui@idc.com"

In [9]: idc.save()

In [10]: idc
Out[10]: <Idc: 徐汇机房>

In [11]: data = {"name":"奉贤机房","address":"奉贤","phone":"1234567891","email":"fengxina@idc.com"}

In [12]: data
Out[12]:
{'name': '奉贤机房',
 'address': '奉贤',
 'phone': '1234567891',
 'email': 'fengxina@idc.com'}

In [13]: idc = Idc(**data)

In [14]: idc.save()

In [15]: Idc.objects.all()
Out[15]: <QuerySet [<Idc: 徐汇机房>, <Idc: 奉贤机房>]>

In [16]: idc
Out[16]: <Idc: 奉贤机房>

In [18]: from idcs.serializers import IdcSerializer

In [19]: serializer = IdcSerializer(idc)

In [20]: serializer
Out[20]:
IdcSerializer(<Idc: 奉贤机房>):
    id = IntegerField(read_only=True)
    name = CharField()
    address = CharField()
    phone = CharField()
    email = EmailField()
    
In [21]: serializer.data
Out[21]: {'id': 2, 'name': '奉贤机房', 'address': '奉贤', 'phone': '1234567891', 'email': 'fengxina@idc.com'}

    
    
编写api文档
devops/urls.py中:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url,include
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^',include('idcs.urls')),
    url(r'^docs/', include_docs_urls("运维平台接口文档"))
]



如果报错:  AttributeError: 'AutoSchema' object has no attribute 'get_link' 
解决:在配置文件settings.py中重新指定schema_class的配置:
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
}


访问接口文档地址: http://127.0.0.1:8000/docs/



序列化高级用法 
进入 apps目录,创建app
django-admin startapp cabinet

然后移动  cabinet 目录到apps目录下

settings.py 中INSTALLED_APPS 新增配置 
'cabinet.apps.CabinetConfig'


    
主路由urls.py改成如下,项目路由废弃
from django.conf.urls import url,include
from django.contrib import admin
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import  DefaultRouter
from idcs.views import IdcViewset
from cabinet.views import CabinetViewset

route = DefaultRouter()
route.register("idcs", IdcViewset)
route.register("cabinet", CabinetViewset)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^docs/', include_docs_urls("运维平台接口文档")),
    url(r'^', include(route.urls)),
]



模型 cabinet/models.py
from django.db import models
from idcs.models import Idc

class Cabinet(models.Model):
    #关联哪个机房且(一个机房有多个机柜)且foreignkey所在的模型是多,即指向的模型是一(Idc)
    idc     = models.ForeignKey(Idc, verbose_name="所在机房",on_delete=models.CASCADE)
    name    = models.CharField(max_length=255)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "resources_cabinet"
        ordering = ["id"]
        
               
视图cabinet/views.py
from rest_framework import viewsets
from .models import Cabinet
from .serializers import CabinetSerializer


class CabinetViewset(viewsets.ModelViewSet):
    """
    retrieve:返回指定机柜信息
     list:返回指定机柜列表
     update:更新机柜信息
     destroy:删除机柜记录
     create:创建机柜记录
     partial_update:更新部分字段
    """
    queryset = Cabinet.objects.all()
    serializer_class = CabinetSerializer

    
序列化 cabinet/serializers.py
from rest_framework import serializers
from idcs.serializers import IdcSerializer
from idcs.models import Idc
from .models import Cabinet


class CabinetSerializer(serializers.Serializer):
#通过主键关联的类型--且当前的这条记录对应idc中是单条记录many指定,且要指定和谁关联queryset
    idc = serializers.PrimaryKeyRelatedField(many=False, queryset=Idc.objects.all())
    name = serializers.CharField(required=True)

    def to_representation(self, instance):
        idc_obj = instance.idc
        ret = super(CabinetSerializer, self).to_representation(instance)
        ret["idc"] = {
            "id": idc_obj.id,
            "name": idc_obj.name
        }
        return ret
#此方法是反序列化的第一步:它拿到的是提交过来的原始数据即QueryDict => request.GET,request.POST,body
    def to_internal_value(self, data):
        """
        反序列化第一步:拿到的是提交过来的原始数据: QueryDict => request.GET, request.POST
        """
        print(data)
        return super(CabinetSerializer, self).to_internal_value(data)
 #create方法要把实例传进去--创建数据
    def create(self, validated_data):
        return Cabinet.objects.create(**validated_data)
  
  

数据库同步           
python manage.py makemigrations cabinet
python manage.py migrate



创建测试数据
python manage.py shell

In [1]: from idcs.models import Idc

In [2]: idc = Idc.objects.get(pk=1)

In [3]: from cabinet.models import Cabinet

In [4]: c = Cabinet()

In [5]: c.idc = idc

In [6]: c.name = "test"

In [7]: c.save()


      
序列化应用之服务器制造厂与型号app功能  
进入 apps目录,创建app
django-admin startapp manufacturer



settings.py 中INSTALLED_APPS 新增配置 
'manufacturer.apps.ManufacturerConfig'



  
主 urls.py路由
from django.conf.urls import url,include
from django.contrib import admin
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import  DefaultRouter
from idcs.views import IdcViewset
from cabinet.views import CabinetViewset
from manufacturer.views import ManufacturerViewset,ProductModelViewset

route = DefaultRouter()
route.register("idcs", IdcViewset)
route.register("cabinet", CabinetViewset)
route.register("manufacturer", ManufacturerViewset)
route.register("productmodel", ProductModelViewset)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^docs/', include_docs_urls("运维平台接口文档")),
    url(r'^', include(route.urls)),
]



模型 manufacturer/models.py
from django.db import models

#制造厂表(一)
class Manufacturer(models.Model):
    vendor_name = models.CharField("厂商名称", max_length=32, db_index=True, unique=True, help_text="厂商名称")
    tel = models.CharField("联系电话", null=True, max_length=15, help_text="联系电话")
    email = models.EmailField("联系邮件", null=True,blank=True,max_length=32, help_text="联系邮件")
    remark = models.CharField("备注", max_length=300,null=True, help_text="备注")
    def __str__(self):
        return self.vendor_name
    class Meta:
        db_table = "resources_manufacturer"
        ordering = ["id"]

#型号表(多)--foreignkey放多中
class ProductModel(models.Model):
    model_name = models.CharField("型号名称",max_length=20, help_text="型号名称")
    vendor = models.ForeignKey(Manufacturer,verbose_name="所属制造商", help_text="所属制造商",on_delete=models.CASCADE)
    def __str__(self):
        return self.model_name
    class Meta:
        db_table = "resources_productmodel"
        #排序
        ordering = ["id"]
        
        

视图 manufacturer/views.py
from django.shortcuts import render
from rest_framework import viewsets
from .models import Manufacturer, ProductModel
from .serializers import ManufacturerSerializer,ProductModelSerializer

class ManufacturerViewset(viewsets.ModelViewSet):
    """
    retrieve:返回指定制造商信息
     list:返回指定制造商列表
     update:更新制造商信息
     destroy:删除制造商记录
     create:创建制造商记录
     partial_update:更新部分字段
    """
    #1.指定queryset
    queryset = Manufacturer.objects.all()
    #2.指定序列化类
    serializer_class = ManufacturerSerializer

class ProductModelViewset(viewsets.ModelViewSet):
    """
    retrieve:返回指定制造商信息
     list:返回指定制造商列表
     update:更新制造商信息
     destroy:删除制造商记录
     create:创建制造商记录
     partial_update:更新部分字段
    """
    #1.指定queryset
    queryset = ProductModel.objects.all()
    #2.指定序列化类
    serializer_class = ProductModelSerializer   



序列化 manufacturer/serializers.py
from rest_framework import serializers
from .models import Manufacturer, ProductModel
#ModelSerializer模型序列化和Serializer序列化的区别是:它有Meta选项,且它帮我们实现了create和update方法,不用再写
class ManufacturerSerializer(serializers.ModelSerializer):

    class Meta:
        #指定它模型是哪个
        model = Manufacturer
        #序列化哪些字段
        fields = "__all__"

class ProductModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductModel
        fields = "__all__"
    # #基于字段级别验证法:如把model_name字段转成大写--这只能是单个字段的,若要多个字段要用模型对象级别验证--表级别验证
    # def validate_model_name(self, value):
    #     return value.upper()
    #基于模型对象级别验证法:
    def validate(self, attrs):
        #取到数据
        manufacturer_obj = attrs["vendor"]
        try:
            manufacturer_obj.productmodel_set.get(model_name__exact=attrs["model_name"])
            raise serializers.ValidationError("该型号已经存在")
        except ProductModel.DoesNotExist:
            return attrs

    def to_representation(self, instance):
        vendor = instance.vendor
        ret = super(ProductModelSerializer, self).to_representation(instance)
        ret["vendor"] = {
            "id": vendor.id,
            "name": vendor.vendor_name
        }
        return ret
        
        
       
数据库同步           
python manage.py makemigrations manufacturer
python manage.py migrate manufacturer


    
用户资源功能app 
进入 apps目录,创建app
django-admin startapp users   



settings.py 中INSTALLED_APPS 新增配置 
'users.apps.UsersConfig'



主 urls.py路由
from django.conf.urls import url,include
from django.contrib import admin
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import  DefaultRouter
from idcs.views import IdcViewset
from cabinet.views import CabinetViewset
from manufacturer.views import ManufacturerViewset,ProductModelViewset
from users.views import UserViewset

route = DefaultRouter()
route.register("idcs", IdcViewset)
route.register("cabinet", CabinetViewset)
route.register("manufacturer", ManufacturerViewset)
route.register("productmodel", ProductModelViewset)
route.register("users", UserViewset)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^docs/', include_docs_urls("运维平台接口文档")),
    url(r'^', include(route.urls)),
]



模型内置已写所以 users/models.py 为空


视图 users/views.py
from django.shortcuts import render
from rest_framework import viewsets
#from django.contrib.auth.models import User 以后建议换成下面这种写法
from django.contrib.auth import get_user_model
from .serializers import UserSerializer

User = get_user_model()
class UserViewset(viewsets.ReadOnlyModelViewSet):
    '''
    这个用户资源的viewset会给外面暴露两个接口retrieve和list
    retrieve:
        返回指定用户信息对象--单个对象的字段是在用户序列化类serializers.py中定义
     list:
        返回用户列表--列表的字段的字段是在用户序列化类serializers.py中定义
    '''
    #1.指定queryset
    queryset = User.objects.all()
    #2.指定序列化类
    serializer_class = UserSerializer
    

序列化  users/serializers.py
from rest_framework import serializers

class UserSerializer(serializers.Serializer):
    """
    用户序列化类
    """
    id          = serializers.IntegerField()
    username    = serializers.CharField()
    email       = serializers.EmailField()


    
生成测试数据
python manage.py shell

In [1]: from django.contrib.auth.models import User

In [2]: def create_user(name):
   ...:     for i in range(1,20):
   ...:         username = "{}-{}".format(name,i)
   ...:         User.objects.create_user(username, "{}@xxl_job.com".format(username),"123456789")
   ...:

In [3]: create_user("xxl")

In [4]: create_user("hell")

In [5]: create_user("python")

In [6]: create_user("golang")



访问地址:  http://127.0.0.1:8000/users/



序列化应用之服务器同步功能 

服务器同步伪代码分析:
需求:
从服务器上采集数据,并整理成想要的格式,然后通过api插入到数据库中(数据库中无此条数据:那需要新增加一条完整的数据。
有此条数据则类型巡检,那更新下服务器此条记录的部分字段--哪些字段可更新哪些字段不可更新,同时要修改它的时间来保证从运维平台上可看到此服务器新况。
采集服务器数据,并同步到运维平台:
1.api接口拿到数据
2.检查mysql中有无此条记录
  怎么检查:
    ip-->经常变,所以不适合。
    SN-->物理机有,虚拟机没有(云服务器有,它的SN就是uuid,但vmware/virtbox都没有)。
    uuid--->针对虚拟机
所以在数据库里查询有没有此台服务器关键就是uuid/SN.所以判断依据是:如果SN=uuid那此机为虚拟机
3.获取这条记录:
(1)存在这条记录,则如下处理法:
    则更新数据:hostname ip os mem disk cpu network
    注意:network:
            存在多网卡,多ip的变动
    
(2)不存在这条记录,则如下处理法:
    先层层验证
    创建这条记录--如下这些都需全新创建并关联上彼此间关系--先创建一的对象再创建多的对象(一对多):
        server记录
        服务器厂商记录
        服务器型号记录
        网卡记录
        ip记录
        注意创建顺序(按foreginkey所在模型的foreginkey字段允许不允许为空),若不允许为空(即此字段为必填)则先创建多的对象:
            先厂商(无任何foreignkey依赖)--再型号(关联厂商)--ip(关联/指向网卡)--网卡(关联/指向服务器)--再服务器
        验证条件:
            厂商无任何依赖,所以可在自定义字段时就做验证--把厂商转成instance
            型号因需一厂商对象,所以需在对象级别/整个表级别时做验证
            


进入 apps目录,创建app
django-admin startapp servers   



settings.py 中INSTALLED_APPS 新增配置 
'servers.apps.ServersConfig'



模型 servers/models.py
from django.db import models
from manufacturer.models import Manufacturer, ProductModel
from idcs.models import Idc
from cabinet.models import Cabinet

#制造商(一)与服务器间(多):foreignkey放在多方
#sn是物理机的唯一标识,uuid是虚拟机的唯一标识  server
class Servers(models.Model):
    ip                  = models.CharField("管理ip", max_length=15, db_index=True, unique=True, help_text="管理ip")
    hostname            = models.CharField("主机名", max_length=20, db_index=True, unique=True, help_text="主机名")
    cpu                 = models.CharField("CPU", max_length=50, help_text="CPU")
    mem                 = models.CharField("内存", max_length=32, help_text="内存")
    disk                = models.CharField("磁盘", max_length=200, help_text="磁盘")
    os                  = models.CharField("OS", max_length=50, help_text="OS")
    sn                  = models.CharField("SN", max_length=50, db_index=True, help_text="SN")
    manufacturer        = models.ForeignKey(Manufacturer, verbose_name="制造商", help_text="制造商",on_delete=models.CASCADE)
    model_name          = models.ForeignKey(ProductModel, verbose_name="服务型号", help_text="服务器型号",on_delete=models.CASCADE)
    rmt_card_ip         = models.CharField("远程管理卡IP", max_length=15, db_index=True, unique=True, help_text="远程管理卡IP")
    idc                 = models.ForeignKey(Idc, null=True, verbose_name="所在机房", help_text="所在机房",on_delete=models.CASCADE)
    cabinet             = models.ForeignKey(Cabinet, null=True, verbose_name="所在机柜", help_text="所在机柜",on_delete=models.CASCADE)
    cabinet_position    = models.CharField("机柜内位置", null=True, max_length=20, help_text="机柜内位置")
    uuid                = models.CharField("UUID", db_index=True, unique=True, max_length=50, help_text="UUID")
    last_check          = models.DateTimeField("检测时间", db_index=True, auto_now=True, help_text="检测时间")
    remark              = models.CharField("备注", max_length=200, help_text="备注", null=True)

    def __str__(self):
        return self.ip

    class Meta:
        db_table = "resources_servers"
        ordering = ["id"]
        permissions = (
            ("view_server", "can view server"),
        )


class NetworkDevice(models.Model):
    """
    网卡
    """
    name        = models.CharField("网卡设备名", max_length=20, help_text="网卡设备名")
    mac_address = models.CharField("MAC地址", max_length=30, help_text="MAC地址")
    host        = models.ForeignKey(Servers, verbose_name="所在服务器", help_text="所在服务器",on_delete=models.CASCADE)
    remark      = models.CharField("备注", max_length=200, help_text="备注", null=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = "resources_network_device"
        ordering = ["id"]

class IP(models.Model):
    """
    IP
    """
    ip_addr     = models.CharField("ip地址", max_length=15, db_index=True, unique=True, help_text="ip地址")
    netmask     = models.CharField("子网掩码", max_length=15, help_text="子网掩码")
    device      = models.ForeignKey(NetworkDevice, verbose_name="所在网卡", help_text="所在网卡",on_delete=models.CASCADE)
    remark      = models.CharField("备注", max_length=200, help_text="备注", null=True)

    def __str__(self):
        return self.ip_addr

    class Meta:
        db_table = "resources_ip"
        ordering = ["id"]
        
        
        
数据库同步           
python manage.py makemigrations servers
python manage.py migrate servers



序列化 servers/serializers.py
from rest_framework import serializers
from .models import Manufacturer, ProductModel
from rest_framework import  serializers
from .models import Servers,NetworkDevice,IP

#这是第一步验证:因为涉及到多张表,所以使用模型序列化不合适(模型序列化它会去校验你的每一字段必须在模型中存在,而我这里的网卡,ip字段是不存在的,都有关联关系),就用序列化--则每个字段要定义一遍
class ServerAutoReportSerializer(serializers.Serializer):
    """
    服务器同步序列化类   
    """
    ip          = serializers.IPAddressField(required=True)
    hostname    = serializers.CharField(required=True, max_length=20)
    cpu         = serializers.CharField(required=True, max_length=50)
    mem         = serializers.CharField(required=True, max_length=20)
    disk        = serializers.CharField(required=True, max_length=200)
    os          = serializers.CharField(required=True, max_length=50)
    sn          = serializers.CharField(required=True, max_length=50)
    manufacturer= serializers.CharField(required=True)
    model_name  = serializers.CharField(required=True)
    uuid        = serializers.CharField(required=True, max_length=50)

#注意提交过来的上述数据中manufacturer是一个普通字符串数据,所以可在验证时就转换--把提交过来的字符串数据转换成对象
#这是第二步验证:自定义字段级别验证--验证传过来的这个制造商数据存在制造商表中否--此方法会返回一个制造商的对象出来
    def validate_manufacturer(self, value):
        #存在则直接返回制造商对象,不存在则创建
        try:
            return Manufacturer.objects.get(vendor_name__exact=value)
        except Manufacturer.DoesNotExist:
            return self.create_manufacturer(value)
#这是第三步验证:
    def validate(self, attrs):
        #拿到一制造商对象
        manufacturer_obj = attrs["manufacturer"]
        #拿到制造商表中制造商所有型号并验证传来的制造商是否存在表中:
        try:
            attrs["model_name"] = manufacturer_obj.productmodel_set.get(model_name__exact=attrs["model_name"])
        #没则报此异常并去创建:
        except ProductModel.DoesNotExist:
            attrs["model_name"] = self.create_product_model(manufacturer_obj, attrs["model_name"])
        return attrs
    #此时的数据是已经经过验证的数据了:
    def create(self, validated_data):
        return Servers.objects.create(**validated_data)

    # 创建一个制造商记录(拿着传过来的制造商名字就创建一制造商记录)---它会返回一制造商对象来--注意这manufacturer这张表只人vendor是不为空,其它字段都可为空:
    def create_manufacturer(self, vendor_name):
        return Manufacturer.objects.create(vendor_name=vendor_name)
    #同理:传一制造商和模型
    def create_product_model(self,manufacturer_obj, model_name):
        return ProductModel.objects.create(model_name=model_name, vendor=manufacturer_obj)

class ServerSerializer(serializers.ModelSerializer):
    """
    服务器序列化类
    """
    class Meta:
        model =  Servers
        fields = "__all__"

class NetworkDeviceSerializer(serializers.ModelSerializer):
    """
    网卡序列化
    """
    class Meta:
        model =  NetworkDevice
        fields = "__all__"

class IPSerializer(serializers.ModelSerializer):
    """
    IP序列化
    """
    class Meta:
        model =  IP
        fields = "__all__"
        
        
           
视图 servers/views.py
from django.shortcuts import render

# Create your views here.
from rest_framework import viewsets, mixins
from .models import Servers,NetworkDevice,IP
from .serializers import ServerAutoReportSerializer,NetworkDeviceSerializer,IPSerializer, ServerSerializer
# from django_filters.rest_framework import DjangoFilterBackend
# from .filter import ServerFilter

#服务器们自动同步接--只支持POST数据(如准备好一脚本每10分钟同步服务器数据一次)--这里已服务器为准,服务器数据变更,我运维平台立即变更
class ServerAutoReportViewset(mixins.CreateModelMixin,viewsets.GenericViewSet):
    """
    create:
        创建一个服务器
    """
    queryset = Servers.objects.all()
    serializer_class = ServerAutoReportSerializer
class ServerViewset(viewsets.ReadOnlyModelViewSet):
    """
    list:
        列出所有服务器信息
    retrieve:
        读取一个服务器信息
    """
    queryset = Servers.objects.all()
    serializer_class = ServerSerializer

#网卡是只读的
class NetworkDeviceViewset(viewsets.ReadOnlyModelViewSet):
    """
    list:
        列出所有网卡信息
    retrieve:
        读取一个网卡信息
    """
    queryset = NetworkDevice.objects.all()
    serializer_class = NetworkDeviceSerializer


#ip是只读的
class IPViewset(viewsets.ReadOnlyModelViewSet):
    """
    retrieve:
        返回一个ip信息
    list:
        列出所有IP信息
    """
    queryset = IP.objects.all()
    serializer_class = IPSerializer
        


路由 urls.py
from django.conf.urls import url,include
from django.contrib import admin
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import  DefaultRouter
from idcs.views import IdcViewset
from cabinet.views import CabinetViewset
from manufacturer.views import ManufacturerViewset,ProductModelViewset
from users.views import UserViewset
from servers.views import ServerAutoReportViewset,NetworkDeviceViewset,IPViewset,ServerViewset

route = DefaultRouter()
route.register("idcs", IdcViewset)
route.register("cabinet", CabinetViewset)
route.register("manufacturer", ManufacturerViewset)
route.register("productmodel", ProductModelViewset)
route.register("users", UserViewset)
route.register("ServerAutoReport", ServerAutoReportViewset)
route.register("NetworkDevice", NetworkDeviceViewset)
route.register("IP", IPViewset)
route.register("Servers", ServerViewset)


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^docs/', include_docs_urls("运维平台接口文档")),
    url(r'^', include(route.urls)),
]



测试:取到服务器以下数据并到测试页面提交测试:

cat /proc/cpuinfo                            #  取cpu
dmidecode --string system-uuid               #  取uuid
dmidecode --string baseboard-manufacturer    #  取制造商
dmidecode --string baseboard-product-name    #  取产品型号

最终取到如下的数据:
{
    "ip": "192.168.1.2",
    "hostname": "yz-ms-web-01",
    "cpu": "Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz 1",
    "mem": "2G",
    "disk": "100G",
    "os": "centos 7",
    "sn": "B49479B2-D774-4B09-B7B9-F29384350C01",
    "manufacturer": "Oracle Corporation",
    "model_name": "VirtualBox",
    "uuid": "B49479B2-D774-4B09-B7B9-F29384350C01"
}



打开如下网址,提交如上的数据,即可入库
http://127.0.0.1:8000/ServerAutoReport/



添加服务器之处理网卡
准备序列化 servers/seriliaizers.py:
添加网卡字段:它的数据类型是json
class ServerAutoReportSerializer(serializers.Serializer):
    # 添加网卡 
    network     = serializers.JSONField(required=True)

未完成,参考 https://www.cnblogs.com/dbslinux/p/13098378.html




服务器之分页和搜索应用 
视图 users/views.py 中导入分页类
from django.shortcuts import render
from rest_framework import viewsets
#from django.contrib.auth.models import User 以后建议换成下面这种写法
from django.contrib.auth import get_user_model
from .serializers import UserSerializer
from rest_framework.pagination import PageNumberPagination


User = get_user_model()
class UserViewset(viewsets.ReadOnlyModelViewSet):
    '''
    这个用户资源的viewset会给外面暴露两个接口retrieve和list
    retrieve:
        返回指定用户信息对象--单个对象的字段是在用户序列化类serializers.py中定义
     list:
        返回用户列表--列表的字段的字段是在用户序列化类serializers.py中定义
    '''
    #1.指定queryset
    queryset = User.objects.all()
    #2.指定序列化类
    serializer_class = UserSerializer
    #引用分页类:
    pagination_class = PageNumberPagination    


    
settings.py 中配置drf分页的每页数据量:
REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'PAGE_SIZE':10
}



打开网址,即可看到分页效果: http://127.0.0.1:8000/users



自定分页器高级用法

(1)app/users/pagination.py
from rest_framework.pagination import PageNumberPagination

class Pagination(PageNumberPagination):
    def get_page_size(self, request):
        try:
            page_size = int(request.query_params.get("page_size", -1))
            if page_size < 0:
                return self.page_size
            return page_size
        except:
            pass
        return self.page_size


(2)让分页功能全局生效settings.py中
这样不用在每个应用的视图中都导入分页了
REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'PAGE_SIZE':10,
    'DEFAULT_PAGINATION_CLASS':'users.pagination.Pagination'
}



若你不希望项目的某个应用不需要分页效果,那么在该应用的视图中viewset类中设置为None即可如下,这样非常灵活

from .pagination import Pagination

class UserViewset(viewsets.ReadOnlyModelViewSet):
    pagination_class = None

    

搜索
使用django-filters做高级搜索并全局使用
参考官网https://django-filter.readthedocs.io/en/master/

需要先安装组件
pip install django-filter



settings.py 中INSTALLED_APPS 新增配置
INSTALLED_APPS = [
    ...
    'django_filters',
]


REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'PAGE_SIZE':10,
    'DEFAULT_PAGINATION_CLASS':'users.pagination.Pagination',
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    )
}



新增 users/filters.py
import django_filters
from django.contrib.auth import get_user_model
from django.db.models import Q
User = get_user_model()
class UserFilter(django_filters.FilterSet):
    #(3)具体怎么搜索法:这里是按字符串搜索并作用在username字段上,并模糊匹配--不区分大小写
    def search_hostname(self,queryset,name,value):
        return queryset.filter(Q(username__icontains=value)|Q(email__icontains=value))
    class Meta:
        model = User
        #(2)按哪字段搜索
        fields = ["username","email"]


        
视图 users/views.py
from django.shortcuts import render
from rest_framework import viewsets
#from django.contrib.auth.models import User 以后建议换成下面这种写法
from django.contrib.auth import get_user_model
from .serializers import UserSerializer
from rest_framework.pagination import PageNumberPagination
from .pagination import Pagination
from  django_filters.rest_framework import DjangoFilterBackend
from .filters import UserFilter

User = get_user_model()
class UserViewset(viewsets.ReadOnlyModelViewSet):
    '''
    这个用户资源的viewset会给外面暴露两个接口retrieve和list
    retrieve:
        返回指定用户信息对象--单个对象的字段是在用户序列化类serializers.py中定义
     list:
        返回用户列表--列表的字段的字段是在用户序列化类serializers.py中定义
    '''
    #1.指定queryset
    queryset = User.objects.all()
    #2.指定序列化类
    serializer_class = UserSerializer
    #引用分页类:
    pagination_class = PageNumberPagination 
    #(2)按哪个字段搜索:
    filter_fields = ('username',"email")
    #(3)使用哪个filter类
    filter_class = UserFilter
    
   
   
别的app应用中也可同样定义如下 servers/filters.py  中并在它自己的视图中调用即可

import django_filters
from .models import Server
from django.db.models import Q

class ServerFilter(django_filters.FilterSet):
    #按主机名搜索
    # hostname = django_filters.CharFilter(method="search_hostname")
    #不光按主机名还可按ip搜索--Q搜索
    def search_hostname(self, queryset, name, value):
        return queryset.filter(Q(hostname__icontains=value)|Q(ip__icontains=value))

    class Meta:
        model = Server
        fields = ['hostname','ip']

        
    
django权限管理/drf权限管理 

1.什么是权限管理
权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源


2.django权限机制
django权限机制能够约束用户行为,控制页面的显示内容,也能使API更加安全和灵活
用好权限机制,能让系统更加强大和健壮


3.django权限控制
django用user, group和permission完成了权限机制,
这个权限机制是将属于model的某个permission赋予user或group,可以理解为全局的权限,
即如果用户A对数据模型(model)B有可写权限,那么A能修改model B的所有实例(objects)。
group的权限也是如此,如果为group C 赋予model B的可写权限,则隶属于group C 的所有用户,都可以修改model B的所有实例。


4.Django的权限项
Django用permission对象存储权限项,每个model默认都有三个permission,即add model, change model和delete model
permission总是与model对应的,如果一个object不是model的实例,我们无法为它创建/分配权限


5.默认权限
在 INSTALLED_APPS 设置中列出 django.contrib.auth 后,
安装的各个应用中的每个 Django 模型默认都有三个权限:添加、修改和删除。
每次运行 manage.py migrate 命令  创建新模型时都会为其赋予这三个权限。


6.分组
django.contrib.auth.models.Group 模型是为用户分类的通用方式,这样便可以为一批用户赋予权限或添加其 他标注。
用户所属的分组数量不限。一个分组中的用户自动获得赋予那个分组的权限。
除了权限之外,分组还是为用户分类的便捷方式,分组后可以给用户添加标签,或者扩展功能


7.权限应用
Permission
User Permission
Group Permission


8.权限检查
Permission:Django定义每个model后,默认都会添加该model的add, change和delete三个permission,
自定义的permission可以在我们定义model时手动添加

class Server(models.Model):
    ...    
    class Meta:        
        permissions = (            
            ("view_server", "can view server"),            
            ("change_server_status", "Can change the status of server"),        
        )

每个permission都是django.contrib.auth.Permission类型的实例,
该类型包含三个字段name, codename 和 content_type,
content_type反应了permission属于哪个model,
codename 如上面的view_server,代码逻辑中检查权限时要用, 
name是permission的描述,将permission打印到屏幕或页面时默认显示的就是name


9.User Permission

User对象的user_permission字段管理用户的权限:
user = User.objects.get(username="rock")
user.user_permissions = [permission_list]
user.user_permissions.add(permission, permission, …)      #增加权限 
user.user_permissions.remove(permission, permission, …)   #删除权限 
user.user_permissions.clear()                             #清空权限
user.get_all_permissions()                                #列出用户的所有权限
user.get_group_permissions()                              # 列出用户所属group的权限


10.Group Permission
group permission管理逻辑与user permission管理一致,group中使用permissions字段做权限管理: 
group.permissions.set([permission_list])
group.permissions.add(permission, permission, …) 
group.permissions.remove(permission, permission, …) 
group.permissions.clear()


11.权限验证-普通视图
在视图中验证权限—— permission_required
当业务逻辑中涉及到权限检查时,decorator能够分离权限验证和核心的业务逻辑,使代码更简洁,逻辑更清晰。
permission的decorator为permission_required

from django.contrib.auth.decorators import login_required, permission_required 
@login_required   #表示你必须要登录
@permission_required('dashboard.view_server')  #表示你必须有权限才可以
def my_view(request):
    ...

    
12.权限验证-类视图

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required, permission_required
class ServerView(TemplateView):
    @method_decorator(login_required)
    @method_decorator(permission_required("dashboard.view_server")
    def get(self, request, *args, **kwargs):
        ...

        
13.权限验证-view代码中验证
if not request.user.has_perm('dashboard.view_server')
    return HttpResponse('Forbidden')

    
14.权限验证-模板中验证
验证是否有登陆 
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p> {
% endif %} 

验证是否有权限 
{% if perms.dashboard.view_server %} 
有权限 
{% endif %}


15.自定义PermissionRequiredMixin
创建仅限
在模型的 Meta 类中定制权限
class Meta:
    permissions = (
        ("modify_user_status", "修改用户状态"),
        ("modify_user_passwd", "修改用户密码"),
    ) 


    
简单权限应用,只有登录过的用户才能访问
(1)apps/users/views.py
from django.shortcuts import render
from rest_framework import viewsets
#from django.contrib.auth.models import User 以后建议换成下面这种写法
from django.contrib.auth import get_user_model
from .serializers import UserSerializer
from rest_framework.pagination import PageNumberPagination
from .pagination import Pagination
from  django_filters.rest_framework import DjangoFilterBackend
from .filters import UserFilter
from rest_framework.permissions import IsAuthenticated

User = get_user_model()
class UserViewset(viewsets.ReadOnlyModelViewSet):
    '''
    这个用户资源的viewset会给外面暴露两个接口retrieve和list
    retrieve:
        返回指定用户信息对象--单个对象的字段是在用户序列化类serializers.py中定义
     list:
        返回用户列表--列表的字段的字段是在用户序列化类serializers.py中定义
    '''
    #1.指定queryset
    queryset = User.objects.all()
    #2.指定序列化类
    serializer_class = UserSerializer
    #引用分页类:
    pagination_class = PageNumberPagination 
    #(2)按哪个字段搜索:
    filter_fields = ('username',"email")
    #(3)使用哪个filter类
    filter_class = UserFilter
    permission_classes = (IsAuthenticated,)
   
   
    
此时打开 http://127.0.0.1:8000/users/ 网页,
会显示: "detail": "Authentication credentials were not provided."
提示必须得认证才能访问,怎么认证?得用drf的认证。



(2)devops/urls.py中添加路由 

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^docs/', include_docs_urls("运维平台接口文档")),
    url(r'^', include(route.urls)),
    url(r'^aip-auth', include("rest_framework.urls",namespace="reset_framework")),
]



创建超级管理员
python manage.py createsuperuser



全局权限应用,模型权限(必须跟模型关联):
(1)settins.py:配置全局模型权限
查看参考源码配置如下图:但是allowany所有人均可访问不是我要的,
所以覆盖它默认配置,改为django的模型权限,这样就全局生效,
好处是不用在每个app的视图中都写,但不好的是如果你一但写一个filter,没有queryset就会出错
解决:就是在app中自定不使用全局的,使用自己独立的即可,这里先使用全局的即可,
settings.py 中只做如下配置,app的views.py中不作任何配置。

REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'PAGE_SIZE':10,
    'DEFAULT_PAGINATION_CLASS':'users.pagination.Pagination',
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
 'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissions',
    ],
}



创建普通用户
python manage.py shell

In [1]: from django.contrib.auth.models import User                  
In [2]: User.objects.create_user("myuser001","myuser001@python.com","123456")



登录后效果:所有的测试页面都只有看的权限



drf自定义get权限
因为django默认支持的模型权限中没有get的,所以可直接自定并覆盖它默认的,

(1)devops/permissions.py 中
from rest_framework.permissions import DjangoModelPermissions

class Permissions(DjangoModelPermissions):
    def get_custom_perms(self, view, method):
        if hasattr(view, "extra_perm_map"):
            if isinstance(view.extra_perm_map, dict):
                return view.extra_perm_map.get(method,[])
        return []

    #这个方法必须写,我是直接把django模型权限中的必备的has_permission方法拷过来
    def has_permission(self, request, view):
        if getattr(view, '_ignore_model_permissions', False):
            return True
        if not request.user or (
           not request.user.is_authenticated and self.authenticated_users_only):
            return False
        queryset = self._queryset(view)
        perms = self.get_required_permissions(request.method, queryset.model)
        perms.extend(self.get_custom_perms(view, request.method))
        return request.user.has_perms(perms)



(2)settings.py 中全局配置替换掉
REST_FRAMEWORK = {
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'PAGE_SIZE':10,
    'DEFAULT_PAGINATION_CLASS':'users.pagination.Pagination',
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
 'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.DjangoModelPermissions',
        'devops.permissions.Permissions',
    ]
}



不使用queryset即模型的权限--viewset权限
class DashboardStatusViewset(viewsets.ViewSet):
    """
    list:
    获取dashboard状态数据
    """
    permission_classes = (permissions.IsAuthenticated,)
    def list(self, request, *args, **kwargs):
        data = self.get_content_data()
        return response.Response(data)

    def get_content_data(self):
        return {
            "aa": 11,
            "bb": 22
        }
        
        

 

posted @ 2021-01-18 10:43  屠魔的少年  阅读(8)  评论(0)    收藏  举报