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
}