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