Django 使用xadmin定制简版cmdb

安装

安装xadmin相对比较简单(python2.x)

pip install xadmin
pip install django==1.9
pip install pymysql

 

python3.6

$ pip install xadmin

报错如下:
long_description=open('README.rst').read(),
    UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 3444: illegal multibyte sequence

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in C:\Users\pinsily\AppData\Local\Temp\pip-build-xzvebsss\xadmin\

#问题解决
到 https://github.com/sshwsfc/xadmin 下载xadmin包
创建空白 README.rst 替换原来的 README.rst并压缩成zip包
pip install D:\\xadmin.zip 安装

$ pip install django==1.11.6

$ pip install pymysql

 

安装完成后,xadmin默认在/usr/lib/python2.x(3.x)/site-packeages/xadmin目录下

 

使用

首先创建一个django项目

django-admin startproject cmdb

 

配置django 加载xadmin app

$ cat cmdb/settings.py

import os
from django.utils.translation import ugettext_lazy as _ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = 'z$%pa&ryr-f6$i6b9+t@29u+w^$mai1$3@24*sgv)5sc-on0@q' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'xadmin',                  #添加,注册xadmin app 'crispy_forms', ] MIDDLEWARE_CLASSES = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'cmdb.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'cmdb.wsgi.application' # Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases #DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } #}

#配置mysql数据库 DATABASES
= { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'testcmdb', 'USER': 'cmdb', 'PASSWORD': '123456', 'HOST': '192.168.177.33', 'PORT': '3306' } } AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] #LANGUAGE_CODE = 'en-us'
#添加中文语言支持 LANGUAGE_CODE = 'zh-hans' LANGUAGES = [ ('en', _('English')), ('zh-hans', _('Simplified Chinese')), ('zh-hant', _('Traditional Chinese'
)), ] TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True STATIC_URL = '/static/'

 

url设置

$ cat cmdb/urls.py

from django.conf.urls import url
from django.contrib import admin
import xadmin

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

 

引用mysql模块配置

$ cat cmdb/__init__.py
import pymysql
pymysql.install_as_MySQLdb() 

 

配置数据库

#创建用户
$ grant all privileges on *.* to 'cmdb'@'%' identified by '123456'

#创建数据库
create database testcmdb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

 

初始化xadmin数据库

$ cd cmdb

$ python manage.py makemigrations xadmin
$ python manage.py migrate xadmin 

#创建超级用户
python manage.py createsuperuser

#注意:python2.x 创建的时候,提示last_login不能为空,导致创建不成功:解决办法:

登录数据库执行
use cmdb
alter table auth_user modify column last_login datetime default null ;

 

创建完用用户后,启动django项目

python manage.py runserver 0.0.0.0:8000

 

登录成功后提示如下错误

AttributeError at /admin/
'WSGIRequest' object has no attribute 'user'
Request Method: GET
Request URL: http://localhost:8000/admin/
Django Version: 1.9.2
Exception Type: AttributeError
Exception Value: 
'WSGIRequest' object has no attribute 'user'
Exception Location: C:\Python27\lib\site-packages\django\contrib\admin\sites.py in has_permission, line 162
Python Executable: C:\Python27\python.exe

问题解决:

settings.py下 MIDDLEWARE配置的有问题,1.10之前,中间件的key为MIDDLEWARE_CLASSES, 1.10之后,为MIDDLEWARE。

MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

 

打开页面登录如下所示

创建cmdb

创建数据库模型

[root@master xadmin]# cd  /usr/lib/python2.7/site-packages/xadmin
[root@master xadmin]# cat models.py
#!/usr/bin/env python
#-*-coding:UTF-8-*-

import json
import django
from django.db import models
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.base import ModelBase
from django.utils.encoding import smart_unicode

from django.db.models.signals import post_migrate
from django.contrib.auth.models import Permission

import datetime
import decimal

if 4 < django.VERSION[1] < 7:
    AUTH_USER_MODEL = django.contrib.auth.get_user_model()
else:
    AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')


def add_view_permissions(sender, **kwargs):
    """
    This syncdb hooks takes care of adding a view permission too all our
    content types.
    """
    # for each of our content types
    for content_type in ContentType.objects.all():
        # build our permission slug
        codename = "view_%s" % content_type.model

        # if it doesn't exist..
        if not Permission.objects.filter(content_type=content_type, codename=codename):
            # add it
            Permission.objects.create(content_type=content_type,
                                      codename=codename,
                                      name="Can view %s" % content_type.name)
            #print "Added view permission for %s" % content_type.name

# check for all our view permissions after a syncdb
post_migrate.connect(add_view_permissions)

class Bookmark(models.Model):
    title = models.CharField(_(u'Title'), max_length=128)
    user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"), blank=True, null=True)
    url_name = models.CharField(_(u'Url Name'), max_length=64)
    content_type = models.ForeignKey(ContentType)
    query = models.CharField(_(u'Query String'), max_length=1000, blank=True)
    is_share = models.BooleanField(_(u'Is Shared'), default=False)

    @property
    def url(self):
        base_url = reverse(self.url_name)
        if self.query:
            base_url = base_url + '?' + self.query
        return base_url

    def __unicode__(self):
        return self.title

    class Meta:
        verbose_name = _(u'Bookmark')
        verbose_name_plural = _('Bookmarks')


class JSONEncoder(DjangoJSONEncoder):
    def default(self, o):
        if isinstance(o, datetime.date):
            return o.strftime('%Y-%m-%d')
        elif isinstance(o, datetime.datetime):
            return o.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(o, decimal.Decimal):
            return str(o)
        elif isinstance(o, ModelBase):
            return '%s.%s' % (o._meta.app_label, o._meta.model_name)
        else:
            try:
                return super(JSONEncoder, self).default(o)
            except Exception:
                return smart_unicode(o)


class UserSettings(models.Model):
    user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"))
    key = models.CharField(_('Settings Key'), max_length=256)
    value = models.TextField(_('Settings Content'))

    def json_value(self):
        return json.loads(self.value)

    def set_json(self, obj):
        self.value = json.dumps(obj, cls=JSONEncoder, ensure_ascii=False)

    def __unicode__(self):
        return "%s %s" % (self.user, self.key)

    class Meta:
        verbose_name = _(u'User Setting')
        verbose_name_plural = _('User Settings')


class UserWidget(models.Model):
    user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"))
    page_id = models.CharField(_(u"Page"), max_length=256)
    widget_type = models.CharField(_(u"Widget Type"), max_length=50)
    value = models.TextField(_(u"Widget Params"))

    def get_value(self):
        value = json.loads(self.value)
        value['id'] = self.id
        value['type'] = self.widget_type
        return value

    def set_value(self, obj):
        self.value = json.dumps(obj, cls=JSONEncoder, ensure_ascii=False)

    def save(self, *args, **kwargs):
        created = self.pk is None
        super(UserWidget, self).save(*args, **kwargs)
        if created:
            try:
                portal_pos = UserSettings.objects.get(
                    user=self.user, key="dashboard:%s:pos" % self.page_id)
                portal_pos.value = "%s,%s" % (self.pk, portal_pos.value) if portal_pos.value else self.pk
                portal_pos.save()
            except Exception:
                pass

    def __unicode__(self):
        return "%s %s widget" % (self.user, self.widget_type)

    class Meta:
        verbose_name = _(u'User Widget')
        verbose_name_plural = _('User Widgets')

#自定义models
#物理服务器
class Physic_Server(models.Model):
    asset_type_choices = (
            ('server', u'服务器'),
            ('networkdevice', u'网络设备'),
            ('storagedevice', u'存储设备'),
            ('securitydevice', u'安全设备'),
            ('otherdevice', u'其它设备'),
    )
    asset_type = models.CharField(choices=asset_type_choices,verbose_name="设备类型",max_length=64, default='server')
    asset_name = models.CharField(verbose_name=u"设备名称", max_length=32, blank=True, null=True)
    manufactory = models.ForeignKey('Manufactory', verbose_name=u'设备供应商', null=True, blank=True, on_delete=models.CASCADE)
    asset_get_type_choices = (
        (0, "购买"),
        (1, "租赁")
    )
    asset_get_type = models.SmallIntegerField(verbose_name="设备获取类型", choices=asset_get_type_choices, default=0)
    asset_model = models.CharField(verbose_name=u'设备型号', max_length=128, null=True, blank=True)
    asset_sn = models.CharField(u'设备编号', max_length=128, unique=True,null=True)
    asset_ip_address = models.CharField(u'IP地址',max_length=64,blank=True,null=True)
    asset_mac_address = models.CharField(u'MAC地址',max_length=64,unique=True,blank=True,null=True)
    asset_Cpu = models.CharField(u'CPU',max_length=24, blank=True,null=True)
    asset_raid_type = models.CharField(u'raid类型',max_length=512, blank=True,null=True)
    asset_Memory = models.CharField(u'内存大小',max_length=24,blank=True,null=True)
    asset_Disk = models.CharField(max_length=32, verbose_name="硬盘大小(GB)", blank=True,null=True)
    asset_Disk_memo = models.TextField(u'硬盘信息备注', null=True, blank=True)
    status_choices = (
        (0,'在线'),
        (1,'已下线'),
        (2,'未知'),
        (3,'故障'),
        (4,'备用'),
    )
    status = models.SmallIntegerField(verbose_name="状态",choices=status_choices,default=0)
    asset_admin = models.ForeignKey(AUTH_USER_MODEL, verbose_name=u'设备管理员',null=True)
    asset_price = models.CharField(u'设备价格', max_length=8,null=True, blank=True)
    asset_contract = models.ForeignKey('Contract', verbose_name=u'设备合同',null=True, blank=True,on_delete=models.CASCADE)
    asset_idc = models.ForeignKey('IDC', verbose_name=u'IDC机房', null=True, blank=True, on_delete=models.CASCADE)
    asset_trade_date = models.DateField(u'设备购买时间',null=True, blank=True)
    asset_expire_date = models.DateField(u'设备过保时间',null=True, blank=True)

    memo = models.TextField(u'备注', null=True, blank=True)

    class Meta:
        verbose_name = '设备'
        verbose_name_plural = "设备列表"


    def __str__(self):
        return self.asset_name

#服务器类
#@python_2_unicode_compatible
class Server(models.Model):
    hosted_on_server = models.ForeignKey('Physic_Server',null=True, verbose_name="所属服务器",blank=True,on_delete=models.CASCADE)

    server_name = models.CharField(verbose_name=u"虚拟机名称", max_length=32, blank=True, null=True)
    business_unit = models.ForeignKey('BusinessUnit', verbose_name=u'所属业务线', null=True, blank=True,on_delete=models.CASCADE)
    ip_address = models.CharField(u'IP地址',max_length=64,blank=True,null=True)
    mac_address = models.CharField(u'MAC地址',max_length=64,blank=True,null=True)
    Cpu = models.CharField(u'CPU',max_length=24, blank=True,null=True)
    raid_type = models.CharField(u'raid类型',max_length=512, blank=True,null=True)
    Memory = models.CharField(u'内存大小',max_length=24,blank=True,null=True)
    Disk = models.CharField(max_length=32, verbose_name="硬盘大小(GB)", blank=True,null=True)
    Disk_memo = models.TextField(u'硬盘信息备注', null=True, blank=True)
    os_type  = models.CharField(u'操作系统类型',max_length=64, blank=True,null=True)
    os_distribution =models.CharField(u'发型版本',max_length=64, blank=True,null=True)
    os_release  = models.CharField(u'操作系统版本',max_length=64, blank=True,null=True)
    memo = models.TextField(u'备注', null=True, blank=True)

    class Meta:
        verbose_name = '虚拟机'
        verbose_name_plural = "虚拟机"
 #       unique_together = ("hosted_on_server", "server_name")

    def __str__(self):
        return self.server_name

#个人电脑
#@python_2_unicode_compatible
class Personal_device(models.Model):

    sub_assset_type_choices =(
    (0,'台式机'),
    (1,'笔记本')
    )
    system_os_choices = (
        (0,'Windows'),
        (1,'MAC OS'),
    )

    sub_asset_type = models.SmallIntegerField(choices=sub_assset_type_choices,verbose_name="电脑类型",default=0)
    asset_get_type_choices = (
        (0, "购买"),
        (1, "租赁")
    )
    asset_get_type = models.SmallIntegerField(verbose_name="获取类型", choices=asset_get_type_choices, default=0)
    status_choices = (
        (0, '使用中'),
        (1, '未使用'),
        (2, '未知'),
        (3, '故障'),
    )
    status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=0)
    user = models.CharField(verbose_name=u"使用人",max_length=32,blank=True,null=True)
    ip_address = models.CharField(u'IP地址',max_length=64,blank=True,null=True)
    mac_address = models.CharField(u'MAC地址',max_length=64,blank=True,null=True)
    model = models.CharField(verbose_name=u'型号', max_length=128, null=True, blank=True)
    cpu = models.CharField(u'CPU',max_length=24, blank=True,null=True)
    Memory = models.CharField(u'内存',blank=True,max_length=12,null=True)
    Disk = models.CharField(max_length=32, verbose_name="硬盘大小(GB)", blank=True,null=True)
    Disk_memo = models.TextField(u'硬盘信息备注', null=True, blank=True)
    os_type  = models.SmallIntegerField(choices=system_os_choices,verbose_name=u'操作系统',default=0)
    os_release  = models.CharField(u'操作系统版本',max_length=64, blank=True,null=True)
    create_date = models.DateTimeField(verbose_name="购买时间",blank=True, auto_now_add=True)
    price = models.CharField(u'价格', max_length=32, null=True, blank=True)
    memo = models.TextField(u'备注', null=True, blank=True)

    class Meta:
        verbose_name = '办公电脑'
        verbose_name_plural = "办公电脑"
class Manufactory(models.Model):
    manufactory = models.CharField(u'供应商名称',max_length=64, unique=True)
    support_num = models.CharField(u'支持电话',max_length=30,blank=True)
    memo = models.CharField(u'备注',max_length=128,blank=True)
    def __str__(self):
        return self.manufactory
    class Meta:
        verbose_name = '供应商'
        verbose_name_plural = "供应商"

class BusinessUnit(models.Model):
    parent_unit = models.ForeignKey('self',related_name='parent_level',blank=True,null=True,on_delete=models.CASCADE)
    name = models.CharField(u'业务线',max_length=64)

    memo = models.CharField(u'备注',max_length=64, blank=True)
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = '业务线'
        verbose_name_plural = "业务线"
        unique_together = ("parent_unit","name")

#@python_2_unicode_compatible
class Contract(models.Model):
    sn = models.CharField(u'合同号', max_length=128,unique=True)
    name = models.CharField(u'合同名称', max_length=64 )
    memo = models.TextField(u'备注', blank=True,null=True)
    price = models.IntegerField(u'合同金额')
    detail = models.TextField(u'合同详细',blank=True,null=True)
    start_date = models.DateField(blank=True,)
    end_date = models.DateField(blank=True)
    license_num = models.IntegerField(u'license数量',blank=True,null=True)
    create_date = models.DateField(auto_now_add=True)
    update_date= models.DateField(auto_now=True)
    class Meta:
        verbose_name = '合同'
        verbose_name_plural = "合同"
    def __str__(self):
        return self.name
#@python_2_unicode_compatible
class IDC(models.Model):
    name = models.CharField(u'机房名称',max_length=64,unique=True)
    memo = models.CharField(u'备注',max_length=128,blank=True,null=True)
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = '机房'
        verbose_name_plural = "机房"

class EventLog(models.Model):
    name = models.CharField(u'事件名称', max_length=100)
    event_type_choices = (
        (1,u'硬件变更'),
        (2,u'新增配件'),
        (3,u'设备下线'),
        (4,u'设备上线'),
        (5,u'定期维护'),
        (6,u'业务上线\更新\变更'),
        (7,u'其它'),
    )
    event_type = models.SmallIntegerField(u'事件类型', choices= event_type_choices)
    asset = models.ForeignKey('Physic_Server',null=True,on_delete=models.CASCADE)
    component = models.CharField('事件子项',max_length=255, blank=True,null=True)
    detail = models.TextField(u'事件详情')
    date = models.DateTimeField(u'事件时间',auto_now_add=True)
    admin = models.ForeignKey(AUTH_USER_MODEL, verbose_name=u'事件源')
    memo = models.TextField(u'备注', blank=True,null=True)

    def __str__(self):
        return self.name
    class Meta:
        verbose_name = '事件纪录'
        verbose_name_plural = "事件纪录"


    def colored_event_type(self):
        if self.event_type == 1:
            cell_html = '<span style="background: orange;">%s</span>'
        elif self.event_type == 2 :
            cell_html = '<span style="background: yellowgreen;">%s</span>'
        else:
            cell_html = '<span >%s</span>'
        return cell_html % self.get_event_type_display()
    colored_event_type.allow_tags = True
    colored_event_type.short_description = u'事件类型'

 

admin注册models

[root@master xadmin]# cat adminx.py

#/usr/bin/env python
#-*-coding:utf-8-*-

from __future__ import absolute_import
import xadmin
from xadmin.models import *
from xadmin.layout import *
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.utils.translation import ugettext_lazy as _, ugettext
from xadmin import views

class UserSettingsAdmin(object):
    model_icon = 'fa fa-cog'
    hidden_menu = True

xadmin.site.register(UserSettings, UserSettingsAdmin)

class PhysicServerAdmin(object):
    list_display = ('id','asset_name','asset_type','asset_model','asset_sn','asset_ip_address','asset_mac_address', 'asset_Cpu', 'asset_Memory', 'asset_Disk','status')
    search_fields = ['asset_ip_address','status']  # 搜索时可输入的字段内容
    #list_display_links = ('id',)  # 点击id可进入详细界面进行编辑
    list_filter = ['status']
    list_per_page = 20  # 每页显示多少条
    ordering = ('id',)
    show_detail_fields = ['asset_name'] #显示本条数据的所有信息


xadmin.site.register(Physic_Server, PhysicServerAdmin)


class ServerAdmin(object):
    list_display = ('id','server_name','hosted_on_server','ip_address','mac_address', 'Cpu', 'Memory', 'Disk', 'business_unit',)
    search_fields = ['ip_address', 'business_unit']  # 搜索时可输入的字段内容
    #list_display_links = ('id',)  # 点击id可进入详细界面进行编辑
    list_per_page = 20  # 每页显示多少条
    ordering = ('id',)
    show_detail_fields = ['server_name']    #显示本条数据的所有信息
xadmin.site.register(Server, ServerAdmin)

class PersonalAdmin(object):
    list_display = ('user','sub_asset_type','model','ip_address','mac_address', 'cpu', 'Memory', 'Disk', 'os_type','status','create_date')
    search_fields = ['user', 'mac_address','ip_address','status']  # 搜索时可输入的字段内容
    #list_display_links = ('id',)  # 点击id可进入详细界面进行编辑
    list_per_page = 20  # 每页显示多少条
    list_filter = ['status']        #过滤器
    ordering = ('id',)
xadmin.site.register(Personal_device, PersonalAdmin)



xadmin.site.register(BusinessUnit)
xadmin.site.register(IDC)

xadmin.site.register(Contract)
xadmin.site.register(Manufactory)
# xadmin.site.register(Tag)
# xadmin.site.register(Software)


class EventLogAdmin(object):
    list_display = ('name','colored_event_type','asset','component','detail','date','user')  #详细信息界面显示的字段
    search_fields = ('asset',)
    list_filter = ('event_type','component','date','admin')

xadmin.site.register(EventLog, EventLogAdmin)

 

重启cmdb项目打开页面如下所示

 

自定义网站title

admin.py

from xadmin import views
class GlobalSetting(object):
    site_title = "xx资产管理系统"
    site_footer = "xxx"      #

xadmin.site.register(views.CommAdminView, GlobalSetting)

效果如图

 

设置App名称,如下图所示

#apps.py

class XAdminConfig(AppConfig):
    """Simple AppConfig which does not do automatic discovery."""

    name = 'xadmin'
    verbose_name = _("CMDB资产管理")

#__init__.py ,默认已经设置,不需要改动

default_app_config = 'xadmin.apps.XAdminConfig'

 

显示数据详情,如下:

 

点击红色方框中的"i",可查看整条数据的详情,或查看相关外键的详细信息

class PhysicServerAdmin(object):
    list_display = ('id','asset_name','asset_type','asset_model','asset_sn','asset_ip_address','asset_mac_address', 'asset_Cpu', 'asset_Memory', 'asset_Disk','status')
    search_fields = ['asset_ip_address','status']  # 搜索时可输入的字段内容
    #list_display_links = ('id',)  # 点击id可进入详细界面进行编辑(默认的)
    list_filter = ['status']
    list_per_page = 20  # 每页显示多少条
    ordering = ('id',)   #根据id排序
readonly_fields = ('user_email',) #设置只读字段
    show_detail_fields = ['asset_name'] #显示本条数据的所有信息

 

posted @ 2018-12-04 18:20  FRESHMANS  阅读(940)  评论(0)    收藏  举报