django项目 myrunner--- 后台 (草稿)

 

功能描述:

。  用户注册和登录

。  用户权限矩阵表

。  仪器台: 

。  "部署工具安装"  --用户页面配置主机清单, 判断操作系统类别,根据系统不同,将主机分组。然后本项目能够自动检查是否安装了ansible或者salt或者pippet(如果安装了部分或者权限会在界面上显示不带灰的按钮(移动鼠标到按钮(或者绿灯)同时显示所安装的版本号)),同时后面有后面有两个按钮,一个"安装"

     (1)页面填写主机IP(或者导入主机IP)-->系统自动分组,生成windows,linux清单(可自由切换) ---> 清单后面有显示是否安装ansible,salt,puppet,zabbix标志(已装--绿色,未装--灰色) ---> 后面有开关按钮, 管理员控制安装和卸载ansible,salt,puppet(其中的卸载功能是暂时停用服务或者移动服务脚本,已让之处于非运行状态,管理员可以恢复)---> 后面有"手工安装"按钮(如果) --->然后最后面有日志按钮,查看批量安装和手工安装的内容,以及管理锁住和卸载工具的日志内容(日志是一直叠加的)

  (2) 清单下面有"批量安装ansible"按钮(如果已经安装或者锁住的主机将会跳过,不进行ansible安装) ,"批量安装salt"按钮,"批量安装puppet"按钮。成功生成后将会跳转到当前页面,然后安装状态发生改变。同时生成日志按钮,查看安装日志。  

            如果批量安装失败,可以在当前页面进行手工安装

。 "部署脚本" -- 切换到此功能页面,就是真正的安装 其他脚本。

     (1) 首先显示的是主机清单,然后是权限按钮,下面有一个检查ansible或者salt或者puppet运行状态的检查按钮,点击后,重新返回当前页面,同时主机清单右边都会有一个运行状态的亮灯,然后分组显示,然后分开tag (ansible运行,salt运行,puppet运行)   注意ansible页面运行里面也会分组对ansible运行状态和非运行状态的主机进行清单分组。同理,salt,puppet页面也是如此。然后用户可以勾选需要的主机,然后在下面有一个按钮"配置",点击进入配置页面(同时请看下面的注意说明)。同时说明,一些被管理员锁住的服务器,由于无法使用ansible,salt,puppet等进行自动部署,所有在清单后面都会有一个"手工部署"的按钮,然后用户进入到手工部署第一页面,里面有手工下载安装包(里面与安装说明),用户可以在进入到界面后,输入账号密码登录系统,

       页面将会读取ansible或者salt,或者puppet的相关配置文件(一个或者多个),可以在页面进行修改和保存,同时页面后面有重启服务的按钮,如果需要上传附件的话,可以在页面下面点击上传附件的按钮。

      然后最下面有保存,确定,测试,取消 四个按钮。其中测试是指,随机抽取一台机器执行脚本,或者指定的测试服务器,然后查看执行结果。点击测试进去页面之后,后有两个按钮 "使用测试服务器"  "使用清单中的一台服务器"。然后保存试保存配置信息,确定是发起审批,然后是变成执行按钮。(审批是经过部门经理,公司领导,然后是同意)。说明,配置里面必须要一个readme文件,否则系统自动判断没有安装说明,

       注意: 配置页面的内容都会针对ansible服务器进行配置(同理salt,puppet),如果ansible服务器,salt服务器,puppet服务器停止服务,那么在点击了"配置"按钮后,将会进入报错页面404,提示salt或者ansible或者puppet服务器掉线了,请使用页面的检查语句进行检查和启动服务。

    (2)每台服务器都可以由系统管理员锁住某些服务器的安装ansible,salt,puppet和是否允许手工安装的权限。

。  尝试增加自动获取主机的功能(最后再加)

。  "审批流程"  -- 除了部署审批,还会有发布审批,这些都后期添加。审批页面里面主要是方便的查看用户的待办,已办

。 管理后台 ---  (1) 新建流程

                      (2)

 

 

打开  Navicat Data Modeler,创建数据模型: 

   首先我针对"部署工具安装"和'"部署脚本"两大模块进行需求分析和物理、逻辑数据模型分析

1、需求分析,前面也已经大概写了想法,下面确定下来的细化功能就是:

     (1)  主机IP清单手工自动分组(windows和linux)

   (2)    判断主机IP是否安装了ansible,salt,puppet和zabbix

      (3)    管理员可以设置开关,是否允许安装ansible或者salt或者puppet  (安全性要求更高的服务器是不安装这些软件。)

     (4) 管理员可以页面进行卸载已经安装的这些部署工具

     (5) 记录安装时的日志

      (6)  允许手工安装部署工具

 

根据以上的细化功能,可以基本定义好数据模型的主要核心实体:

      主机IP,  (ansible,salt,puppet,zabbix), 手工安装界面

     

那么下面就是确定他们的主键和属性: (下面的是以mysql配置属性的类型)  

---表名: tb_hostip----
ID      主键
主机IP char(15)
主机名 varchar(50)
主机说明 varchar(50) --主机说明,限25个中文字符
主机DNS char(15)
OS_type char(1) 1-- linux 2 --windows 3--others
is_Lock_ansible char(1) 1---Lock 0 ---NotLock --1 锁住代表不允许安装ansible
is_installed_ansible
is_Lock_install_salt char(1) 1---Lock 0 ---NotLock --1锁住代表不允许安装salt
is_installed_salt
is_Lock_install_puppet char(1) 1---Lock 0 ---NotLock --1锁住代表不允许安装puppet
is_installed_puppet
is_Lock_install_zabbix_agent char(1) 1---Lock 0 ---NotLock --1锁住代表不允许安装zabbix agent
is_installed_zabbix
is_Lock_byhand char(1) 1---Lock 0 ---NotLock --1锁住代表不允许手工登录页面进行手工安装
lastlog 是否生成了最新日志
forbbiden_uninstall char(1) 0---全部允许用户卸载, 1--全部禁止用户卸载
pg_id ---外键 项目ID
 

 

2、 确定相关实体: 

 

跟主机IP关联的其他实体有 : 

 

    项目实体: 譬如两书项目可以有多个主机IP组成   即:  项目  ---1:N----主机IP  (1对多的关系)


---表名: tb_program----
 ID     --主键ID
 PG_NAME:    ---项目名字
 PG_DB   --项目分组:  1--程序服务器  2--db数据库  3---缓存服务器  4---
 ServerIP:    ---服务器IP
 ServerDNS   ---服务器DNS

   操作系统实体:  (可以后期不断增加操作系统类型,在页面上显示linux /windows/其他)

ID  --主键名
os_type   --os类型
os_name   --os名字

  

3、确认关联:(尽量满足第三范式)

  (1) tb_hostip 和  tb_program

      字段:  hostip_id  , 

  (2) tb_hostip 和

 

 

 

 

 

本案例的技术架构:

 

Django + rest+angularjs+bootstrap

 

创建Django项目,

    本例子是myrunner,箭头是要注意的地方。

 

 

 在根目录下新建  (后期根据需要还可以继续拓展)

 

  static(目录),    --用来放置静态css,img和js脚本

  common(package)   ---系统的公共模块

  deploy (package)  --- 部署模块 ,今天的重点模块

  system (package)  ---系统管理模块。后期完善

 

 

 然后修改setting的 DATABASES: (本例子是mysql,和python3.5)  安装的是MySQLdb模块

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myrunner',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}

其中 USER和PASSWORD是链接mysql的账号密码,请注意如果是测试环境可以使用root账号登录测试

如果mysql是在其他的服务器上,请修改HOST为对应的mysql服务器的IP地址

 

如果已经安装了pymysql还是报以下错误的:

 

请在__init__.py填写以下内容:

import pymysql
pymysql.install_as_MySQLdb()

 

 

 

生成MODEL:

   使用反向工程原理,在DataModule生成对应的sql:

 

 

将导出来的sql 语句导进去pycharm里面配置好的mysql链接

 

在命令窗口粘贴导出的sql语句,然后执行(注意:请选择正确的数据库,本例子是myrunner数据库) 

发现外键报错: Cannot add foreign key constraint:

这是什么情况了?

后期补上!

 

 

 

然后在django项目的命令行里,执行

   

python manage.py inspectdb > myrunner/models.py 

 将所有的model类生成到一个文件里面,为了方便管理,可以新建model文件,将各表model移到对应的python文件里面。

然后成功显示出model内容: 

 

 

 将此文件移动到deploy目录,因为这个model是针对deploy  发布部署模块的

生成 Serializers:

首先配置好django_rest_framework: 

在  setting里面的INSTALLED_APPS

USER_SETTINGS = getattr(settings, "REST_FRAMEWORK", None)

 



 

第一步我们需要为我们的API提供序列化和反序列化的方法, 将myrunner实例转为json等方式呈现数据. 我们可以使用Serializer达到这一目的, Serializer和django forms十分相似. 我们建立myrunner/serializers.py文件:

serializers.py:

 


    from django.forms import widgets
    from rest_framework import serializers
    from myrunner.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


    class HostIPSerializer(serializers.Serializer):
        pk = serializers.Field()  # `Field` 是无类型, 只读的.
        title = serializers.CharField(required=False,
                                      max_length=100)
        code = serializers.CharField(widget=widgets.Textarea,
                                     max_length=100000)
        linenos = serializers.BooleanField(required=False)
        language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
                                           default='python')
        style = serializers.ChoiceField(choices=STYLE_CHOICES,
                                        default='friendly')

        def restore_object(self, attrs, instance=None):
            """
            创建或更新一个snippet实例, 返回该snippet实例

            如果不定义该function, 则反序列化时将返回一个包括所有field的dict
            """
            if instance:
                # 更新已存在的snippet实例
                instance.title = attrs.get('title', instance.title)
                instance.code = attrs.get('code', instance.code)
                instance.linenos = attrs.get('linenos', instance.linenos)
                instance.language = attrs.get('language', instance.language)
                instance.style = attrs.get('style', instance.style)
                return instance

            # Create new instance
            return Snippet(**attrs)
 

   

以上代码第一部分定义了序列化和反序列化的项, 第二部分restore_object function则定义了符合要求的已序列化snippet实例如何反序列化.

注意, 我们也可以使用在django form中使用的参数, 比如widget=widgets.Textarea. 这些参数可以控制serializer如何显示为HTML form, 尤其是在构建可浏览的API时, 十分有用.

如果想快速的构建serializer, 我们也可以使用ModelSerializer

    #myrunner/serializers.py
    class PostIPSerializer(serializers.ModelSerializer):
        class Meta:
            model = Snippet
            fields = ('id', 'ip', 'hostname', 'hostcomment', 'hostdns', 'os_type',...)

 ...省略。。后期补全

 

 

 

 

 

API Views

 

myrunner目录下的view.py

 


 # snippets/views.py
    from django.http import HttpResponse
    from django.views.decorators.csrf import csrf_exempt
    from rest_framework.renderers import JSONRenderer
    from rest_framework.parsers import JSONParser
    from snippets.models import Snippet
    from snippets.serializers import SnippetSerializer

    class JSONResponse(HttpResponse):
        """
        将内容转为JSON格式的HttpResponse
        """
        def __init__(self, data, **kwargs):
            content = JSONRenderer().render(data)
            kwargs['content_type'] = 'application/json'
            super(JSONResponse, self).__init__(content, **kwargs)



@csrf_exempt
    def snippet_list(request):
        """
        展示所有存在的snippet, 或建立新的snippet
        """
        if request.method == 'GET':
            snippets = Snippet.objects.all()
            serializer = SnippetSerializer(snippets, many=True)
            return JSONResponse(serializer.data)

        elif request.method == 'POST':
            data = JSONParser().parse(request)
            serializer = SnippetSerializer(data=data)
            if serializer.is_valid():
                serializer.save()
                return JSONResponse(serializer.data, status=201)
            return JSONResponse(serializer.errors, status=400)


@csrf_exempt
    def snippet_detail(request, pk):
        """
        展示, 更新或删除一个snippet
        """
        try:
            snippet = Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            return HttpResponse(status=404)

        if request.method == 'GET':
            serializer = SnippetSerializer(snippet)
            return JSONResponse(serializer.data)

        elif request.method == 'PUT':
            data = JSONParser().parse(request)
            serializer = SnippetSerializer(snippet, data=data)
            if serializer.is_valid():
                serializer.save()
                return JSONResponse(serializer.data)
            return JSONResponse(serializer.errors, status=400)

        elif request.method == 'DELETE':
            snippet.delete()
            return HttpResponse(status=204)
 
 

 

urls.py

 

 # myrunner/urls.py
    from django.conf.urls import patterns, url

    urlpatterns = patterns('snippets.views',
        url(r'^snippets/$', 'snippet_list'),
        url(r'^snippets/(?P<pk>[0-9]+)/$', 'snippet_detail'),
    )

 

然后启动django  server和进行验证测试

    python manage.py runserver

使用命令行测试:

 

curl http://127.0.0.1:8000/snippets/

[{"id": 1, "title": "", "code": "foo = \"bar\"\n", "linenos": false, "language": "python", "style":
    "friendly"}, {"id": 2, "title": "", "code": "print \"hello, world\"\n", "linenos": false, "language":
    "python", "style": "friendly"}]

使用浏览器进行测试:

 

 

 

第二部分

以上是简单的django--restwork 测试,下面的内容将会深入分模块进行架构

1、将原来的setting.py的内容分拆出来,生成local.py和common.py,分别保存

 

local.py  ---主要讲ADMINS,DATABASES,和SITES,STATIC_ROOT等入口参数信息独立出来。然后你会有疑问,入库文件名字改了,那如何让django的setting调用新的入口文件local.py (而非之前的settings.py)

 那就要讲解一下django.conf.settings:

 

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

 

代码如下: 

ADMINS = (
   ("Admin", "example@example.com"),
)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'taiga',
        'USER': 'taiga',
        'PASSWORD': 'changeme',
        'HOST': '',
        'PORT': '',
    }
}


SITES = {
   "api": {
      "scheme": "http",
      "domain": "localhost:8000",
      "name": "api"
   },
   "front": {
      "scheme": "http",
      "domain": "localhost:9001",
      "name": "front"
   },
}

SITE_ID = "api"

MEDIA_ROOT = '/myGitHup/myrunnner/media'
STATIC_ROOT = '/myGitHup/myrunnner/static' 

 

 common.py:

 将 REST_FRAMEWORK,INSTALLED_APPS, MIDDLEWARE ,ROOT_URLCONF,TEMPLATES,WSGI_APPLICATION,LOGGING,SESSION_ENGINE,LOCALE_PATHS,CACHES等信息独立出来

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        # Mainly used by taiga-front
        "myrunner.auth.backends.Token",

        # Mainly used for api debug.
        "myrunner.auth.backends.Session",

        # Application tokens auth
        "myrunner.external_apps.auth_backends.Token",
    ),


}

请注意这个自定义的REST_FRAMEWORK变量

 

2、定义各模块的apps.py 

   譬如deploy目录下面增加apps.py, 加载app应用到django里面


from django.apps import AppConfig
from django.conf import settings
from django.conf.urls import include, url
class DeployAppConfig(AppConfig):
    name = "myrunner.Deploy"
    verbose_name = "Deploy App Config"

    def ready(self):
        #connect_events_signals()
        # 如果设置了属性,让模块的url添加到django app的入口url里面。不断的拓展url跳转
if settings.FEEDBACK_ENABLED: from myrunner.urls import urlpatterns from .routers import router urlpatterns.append(url(r'^api/v1/', include(router.urls)))

 

然后在_init__.py添加内容: 

default_app_config = "myrunner.deploy.apps.DeployAppConfig"

 

3、 定义调用前面的common.py定义好settings的REST_FRAMEWORK 的

 

 

"""
Settings for REST framework are all namespaced in the REST_FRAMEWORK setting.
For example your project's `settings.py` file might look like this:

REST_FRAMEWORK = {
    "DEFAULT_RENDERER_CLASSES": (
        "taiga.base.api.renderers.JSONRenderer",
    )
    "DEFAULT_PARSER_CLASSES": (
        "taiga.base.api.parsers.JSONParser",
    )
}

This module provides the `api_setting` object, that is used to access
REST framework settings, checking for user settings first, then falling
back to the defaults.
"""
from __future__ import unicode_literals

from django.conf import settings
from django.utils import six

import importlib

from . import ISO_8601


USER_SETTINGS = getattr(settings, "REST_FRAMEWORK", None)
DEFAULTS = {
    # Base API policies
    "DEFAULT_RENDERER_CLASSES": (
        "taiga.base.api.renderers.JSONRenderer",
    ),
  # Genric view behavior
    "DEFAULT_MODEL_SERIALIZER_CLASS":
        "taiga.base.api.serializers.ModelSerializer",
    "DEFAULT_MODEL_VALIDATOR_CLASS":
        "taiga.base.api.validators.ModelValidator",
    "DEFAULT_FILTER_BACKENDS": (),

    # Throttling
    "DEFAULT_THROTTLE_RATES": {
        "user": None,
        "anon": None,
    },

    # Pagination
    "PAGINATE_BY": None,
    "PAGINATE_BY_PARAM": None,
    "MAX_PAGINATE_BY": None,

    # Authentication
    "UNAUTHENTICATED_USER": "django.contrib.auth.models.AnonymousUser",
    "UNAUTHENTICATED_TOKEN": None,

    # View configuration
    "VIEW_NAME_FUNCTION": "taiga.base.api.views.get_view_name",
    "VIEW_DESCRIPTION_FUNCTION": "taiga.base.api.views.get_view_description",

    # Exception handling
    "EXCEPTION_HANDLER": "taiga.base.api.views.exception_handler",

    # Testing
    "TEST_REQUEST_RENDERER_CLASSES": (
        "taiga.base.api.renderers.MultiPartRenderer",
        "taiga.base.api.renderers.JSONRenderer"
    ),
    "TEST_REQUEST_DEFAULT_FORMAT": "multipart",

    # Browser enhancements
    "FORM_METHOD_OVERRIDE": "_method",
    "FORM_CONTENT_OVERRIDE": "_content",# Input and output formats
    "DATE_INPUT_FORMATS": (
        ISO_8601,
    ),
    "DATE_FORMAT": ISO_8601,

    "DATETIME_INPUT_FORMATS": (
        ISO_8601,
    ),
    "DATETIME_FORMAT": None,

    "TIME_INPUT_FORMATS": (
        ISO_8601,
    ),
    "TIME_FORMAT": None,

    # Pending deprecation
    "FILTER_BACKEND": None,
}


# List of settings that may be in string import notation.
IMPORT_STRINGS = (
    "DEFAULT_RENDERER_CLASSES",
    "DEFAULT_PARSER_CLASSES",
    "DEFAULT_AUTHENTICATION_CLASSES",
    "DEFAULT_PERMISSION_CLASSES",
    "DEFAULT_THROTTLE_CLASSES",
    "DEFAULT_CONTENT_NEGOTIATION_CLASS",
)


def perform_import(val, setting_name):
    """
    If the given setting is a string import notation,
    then perform the necessary import or imports.
    """
    if isinstance(val, six.string_types):
        return import_from_string(val, setting_name)
    elif isinstance(val, (list, tuple)):
        return [import_from_string(item, setting_name) for item in val]
    return val


def import_from_string(val, setting_name):
    """
    Attempt to import a class from a string representation.
    """
    try:
        # Nod to tastypie's use of importlib.
        parts = val.split('.')
        module_path, class_name = '.'.join(parts[:-1]), parts[-1]
        module = importlib.import_module(module_path)
        return getattr(module, class_name)
    except ImportError as e:
        msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
        raise ImportError(msg)


class APISettings(object):
    """
    A settings object, that allows API settings to be accessed as properties.
    For example:

        from taiga.base.api.settings import api_settings
        print api_settings.DEFAULT_RENDERER_CLASSES

    Any setting with string import paths will be automatically resolved
    and return the class, rather than the string literal.
    """
    def __init__(self, user_settings=None, defaults=None, import_strings=None):
        self.user_settings = user_settings or {}
        self.defaults = defaults or {}
        self.import_strings = import_strings or ()

    def __getattr__(self, attr):
        if attr not in self.defaults.keys():
            raise AttributeError("Invalid API setting: '%s'" % attr)

        try:
            # Check if present in user settings
            val = self.user_settings[attr]
        except KeyError:
            # Fall back to defaults
            val = self.defaults[attr]

        # Coerce import strings into classes
        if val and attr in self.import_strings:
            val = perform_import(val, attr)

        self.validate_setting(attr, val)

        # Cache the result
        setattr(self, attr, val)
        return val

    def validate_setting(self, attr, val):
        if attr == "FILTER_BACKEND" and val is not None:
            # Make sure we can initialize the class
            val()

api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS)

 

 4、

 url.py

urlpatterns  根据settings设置来动态添加url

if settings.FRONT_SITEMAP_ENABLED:
    from django.contrib.sitemaps.views import index
    from django.contrib.sitemaps.views import sitemap
    from django.views.decorators.cache import cache_page

    from taiga.front.sitemaps import sitemaps

    urlpatterns += [
        url(r"^front/sitemap\.xml$",
            cache_page(settings.FRONT_SITEMAP_CACHE_TIMEOUT)(index),
            {"sitemaps": sitemaps, 'sitemap_url_name': 'front-sitemap'},
            name="front-sitemap-index"),
        url(r"^front/sitemap-(?P<section>.+)\.xml$",
            cache_page(settings.FRONT_SITEMAP_CACHE_TIMEOUT)(sitemap),
            {"sitemaps": sitemaps},
            name="front-sitemap")
    ]

 

 

5、rest_framework与router分不开

 

  router.py

from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'projectversionViewSet', ProjectVersionViewSet)
router.register(r'projectViewSet', ProjectViewSet)
router.register(r'deployJobViewSet', DeployJobViewSet)

 

可以自定义router:

 

class NestedRouterMixin:
    def _register(self, *args, **kwargs):
        return super().register(*args, **kwargs)

    def register(self, *args, **kwargs):
        self._register(*args, **kwargs)
        return NestedRegistryItem(
            router=self,
            parent_prefix=self.registry[-1][0]
        )
class DRFDefaultRouter(SimpleRouter):
    """
    The default router extends the SimpleRouter, but also adds in a default
    API root view, and adds format suffix patterns to the URLs.
    """
pass --需要知道的请联系我

class
DefaultRouter(NestedRouterMixin, DRFDefaultRouter): pass __all__ = ["DefaultRouter"]

 

6、rest_framework和router跳转到view: 

from rest_framework import viewsets

from deploy_manager.models import *
from deploy_manager.serializers import *


class ProjectVersionViewSet(viewsets.ModelViewSet):
    queryset = ProjectVersion.objects.all()
    serializer_class = ProjectVersionSerializer

 

   实际内容就是model操作数据库和serializer序列化

 

7、view和前端的关联问题; 

  

 

    @detail_route(methods=["POST"])
    def watch(self, request, pk=None):
        project = self.get_object()
        self.check_permissions(request, "watch", project)
        self.pre_conditions_on_save(project)
        notify_level = request.DATA.get("notify_level", NotifyLevel.involved)
        project.add_watcher(self.request.user, notify_level=notify_level)
        return response.Ok()

 

angularjs:

其中的 $http.get('/api/posts') 就是从后台获取数据到前端

app = angular.module 'example.app.basic', []

app.controller 'AppController', ['$scope', '$http', ($scope, $http) ->
    $scope.posts = []
    $http.get('/api/posts').then (result) ->
        angular.forEach result.data, (item) ->
            $scope.posts.push item
]

然后这个 AppController  就是html组件的ID,数据将会显示在这个组件里面

 

base.html: 

<div class="content ng-cloak" ng-controller="{% block ng_controller %}AppController{% endblock %}">{% block content %}

basic.html:

{% extends 'base.html' %}

{% block ng_app %}example.app.basic{% endblock %}

 

 

 

8、admin  --将某些模块注册到admin模块下,已让他们登录后才能打开此模块

  首先某个模块定义好自己的***Admin:

from django.contrib import admin

admin.register(HostIP, HostIPAdmin)

其中

HostIPAdmin.py
import requests
from django.conf.urls import url
from django.contrib import admin
from cmdb.admins import HostAdmin
from cmdb.models import *
from deploy_manager.models import *from saltjob.tasks import scanHostJob
from saltops.settings import SALT_CONN_TYPE, SALT_HTTP_URL, SALT_REST_URL

class RackInline(NestedStackedInline):
    model = Rack
    fields = ['name']
    verbose_name = '机架'
    verbose_name_plural = '机架'
    extra = 0
    fk_name = 'cabinet'


class CabinetInline(NestedStackedInline):
    model = Cabinet
    fields = ['name']
    verbose_name = '机柜'
    verbose_name_plural = '机柜'
    extra = 0
    fk_name = 'idc'
    inlines = [RackInline]


@admin.register(IDC)
class IDCAdmin(NestedModelAdmin):
    list_display = ['name', 'type', 'phone',
                    'linkman', 'address',
                    'operator', 'concat_email', 'cabinet_count', 'create_time', 'update_time']
    search_fields = ['name']
    inlines = [CabinetInline]
    list_filter = ['type']

    def cabinet_count(self, obj):
        return '<a href="/admin/cmdb/cabinet/?q=&idc=%s">%s</a>' % (obj.id, obj.cabinet_set.count())

    cabinet_count.short_description = '机柜数量'
    cabinet_count.allow_tags = True

 

 

如果不定义自己的***Admin,可以继承ModelAdmin: 

或者使用modeladmin:

from django.contrib import admin

from . import models


class FeedbackEntryAdmin(admin.ModelAdmin):
    list_display = ['created_date', 'full_name', 'email' ]
    list_display_links = list_display
    list_filter = ['created_date',]
    date_hierarchy = "created_date"
    ordering = ("-created_date", "id")
    search_fields = ("full_name", "email", "id")


admin.site.register(models.FeedbackEntry, FeedbackEntryAdmin)

 

注意要使用admin:

 请配置好以下内容:

   (1)'django.contrib.admin'加入setting的INSTALLED_APPS配置中。

   (2) 保证INSTALLED_APPS中,包含'django.contrib.auth''django.contrib.contenttypes''django.contrib.messages''django.contrib.sessions.',Django的admin需要这4个包。

settings.py:

  

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'book',
    ] 

   (3) 将'django.contrib.messages.context_processors.messages'添加到 TEMPLATE_CONTEXT_PROCESSORS中,并确保MIDDLEWARE_CLASSES 包含'django.contrib.auth.middleware.AuthenticationMiddleware''django.contrib.messages.middleware.MessageMiddleware'。 (默认已加入)

   (4) 为每个需要admin的app中的admin.py中创建一个ModelAdmin

   (5) 将admin访问配置在URLconf中

    

from django.contrib import admin
admin.autodiscover()

# And include this URLpattern...
urlpatterns = patterns('',
    # ...
    (r'^admin/', include(admin.site.urls)),
    # ...
)

    (6) 运行python manage.py migrate提醒创建superuser后就可以访问 http://127.0.0.1:8000/admin/ 了。其实就是生成表结构的过程:

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

 

    注意: 

Admin是如何工作的

当把'django.contrib.admin'添加到INSTALLED_APPS后,django会自动查找每个app中的admin.py模块并且import载入。

class apps.AdminConfig

Django 1.7新增.

admin默认的AppConfig类.当django启动时会执行autodiscover()。

class apps.SimpleAdminConfig

Django 1.7新增.

类似于AdminConfig,不会执行autodiscover().

autodiscover()

import每个app的admin.py模块。

Django 1.7改变:
在以前的版本中需要在urls.py中手动启动此方法去寻找每个app的admin.py,1.7后AdminConfig会自动执行此方法。

如果正在使用定制的AdminSite,需要将ModelAdmin的子类载入到自己的代码中并全部注册到定制的AdminSite中。这种情况下需要停止自动discovery(),可以将'django.contrib.admin.apps.SimpleAdminConfig'代替INSTALLED_APPS中的'django.contrib.admin'

  详情请看 :  这里

 

 

9、rest_framework的 permissions 和 request (get、post)

 

 

from rest_framework import permissions


class SafeMethodsOnlyPermission(permissions.BasePermission):
    """Only can access non-destructive methods (like GET and HEAD)"""
    def has_permission(self, request, view):
        return self.has_object_permission(request, view)

    def has_object_permission(self, request, view, obj=None):
        return request.method in permissions.SAFE_METHODS

 

 

 

 

posted @ 2017-03-28 09:49  八月的男人  阅读(...)  评论(...编辑  收藏