本站目前的开发环境是 Python3.5.2+Django1.10.6+Sqlite3+Centos7.5+Nginx1.12.2+Gunicorn

blog虽然已经上线测试有一段时间了(2018.8.7上线),但是项目部署的笔记由于内容较多,没时间整理。今天抽空整理了一下午,发布出来供需要的同学借鉴参考。文中如有错误请多多指正

部署前的准备

服务器

要部署项目就要先购买服务器,如腾讯云,阿里云等等

注意:服务器在国内的话,必须要备案!不然无法使用。

阿里云优惠券:点击前往领取
腾讯云代金券:点击前往领取
腾讯云首页:点击直达
腾讯云西南区云产品促销链接:点击直达
腾讯云服务器购买链接:点击直达

域名

域名购买选择,可以到百度云,腾讯云,阿里云,GoDaddy,namecheap等等

购买域名后就是解析域名,打开域名解析面板,添加解析记录
记录类型:A
主机记录:test.markhoo.com,这个markhoo.com是你的一级域名,二级域名你可以设置为www.markhoo.com
解析线路:可以默认也可以选别的
记录值:填写你的服务器IP地址
TTL:域名生效时间,也就是多久生效
设置好以上内容,点击确定。解析完成

主机记录中这个markhoo.com是你的一级域名,二级域名你也可以设置为 www.markhoo.com 或者别的 xxx.markhoo.com 也可以。
注意:域名是否在国内购买不重要,只要服务器不在大陆购买,即使未备案照样可以使用。要是要解析的服务器是大陆服务商则必须要备案。备案后一个域名可以同时接入多个服务商。

域名解析记录类型说明:

  • A:将域名指向一个IPv4地址
  • AAAA:将域名指向一个IPv6地址
  • CNAME:将域名指向另外一个域名
  • NS:将子域名指定其他DNS服务器解析
  • MX:将域名指向邮件服务器地址
  • SRV:记录提供特定的服务的服务器
  • TXT:文本长度限制512,通常做SPF记录(反垃圾邮件)
  • CAA:CA证书颁发机构授权校验
  • 显性URL:将域名302重定向到另外一个地址
  • 隐性URL:与显性URL类似,但是会隐藏真实目标地址

xshell下载与安装

主要用于远程登录服务器进行相关操作。在官网注册后发送下载链接到你的注册邮箱,或者到搜索引擎搜索下载。
个人版免费,企业版收费。
下载后一路点击下一步,完成。

登录服务器步骤:
点击左上角'文件'--'新建'
然后弹出对话框
常规:名称,这个你可以随便取什么名字
协议:SSH,一般默认
主机:空,填上你的服务器IP地址
端口号:22,默认的就行
填好以上选项内容,点击左侧的'用户身份验证',然后看右侧,方法选择'Password',在下面输入你的服务器登录用户名和密码,最后点击下面的'连接'。
登录成功!进入界面。

百度网盘官网软件下载链接:
链接: https://pan.baidu.com/s/19K5q1JkmD9qnYaGRTALyLg 密码: r6hx

安装Nginx

首先用xshell登录服务器
通过 yum 安装 Nginx

yum install nginx

启动 Nginx 服务

systemctl start nginx

或者

systemctl start nginx.service
systemctl enable nginx.service

访问服务器IP地址,可以看到 nginx 的欢迎界面

Nginx相关命令:
停止nginx

nginx -s stop

重启nginx

nginx -s reload

Centos7.5安装Python3

由于centos7.5原本默认安装了Python2,而且这个Python2不能被删除,因为有很多系统命令,比如yum都要用到。
以下操作首先要用xshell登录到服务器
输入Python命令,查看可以得知是Python2.7.5版本

输入

which python

可以查看位置,一般是位于/usr/bin/python目录下。

下面介绍安装Python3的方法

首先安装依赖包

yum -y groupinstall "Development tools"
yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel

然后根据自己需求下载不同版本的Python3,我下载的是Python3.5.2

注:如果速度不够快,可以直接去官网下载,利用WinSCP等软件传到服务器上指定位置。

我的存放目录是/usr/local/python3,使用命令:

mkdir /usr/local/python3

建立好一个空文件夹,进入该目录

cd /usr/local/python3

使用一下命令下载压缩包

wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz

然后解压压缩包安装Python3

tar -xvJf Python-3.5.2.tar.xz
cd Python-3.5.2
./configure --prefix=/usr/local/python3
make && make install

最后创建软链接

ln -s /usr/local/python3/bin/python3 /usr/bin/python3
ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3

在命令行中输入python3测试

升级pip3命令:

pip3 install --upgrade pip

部署前的项目配置

Django 项目中会有一些 CSS、JavaScript 等静态文件,为了能够方便地让 Nginx 处理这些静态文件的请求,我们把项目中的全部静态文件收集到一个统一的目录下,这个目录通常位于 Django 项目的根目录,并且命名为 static。为了完成这些任务,需要在项目的配置文件里做一些必要的配置:

myblog/settings.py

# 其他配置...

STATIC_URL = '/static/'
# 加入下面的配置
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

STATIC_ROOT 指明了静态文件的收集目录,即项目根目录(BASE_DIR)下的 static 文件夹。

为了安全起见,在生产环境下需要关闭 DEBUG 选项以及设置允许访问的域名。打开 settings.py 文件,找到 DEBUG 和 ALLOWED_HOSTS 这两个选项,将它们设置成如下的值:

myblog/settings.py

DEBUG = False
ALLOWED_HOSTS = ['127.0.0.1', 'localhost ', '.markhoo.com']

ALLOWED_HOSTS 是允许访问的域名列表,127.0.0.1 和 localhost 是本地访问的域名,.markhoo.com 是访问服务器的域名(换成你自己的域名)。域名前加一个点表示允许访问该域名下的子域名,比如 www.markhoo.com test.markhoo.com 等二级域名同样允许访问。如果不加前面的点则只允许访问 markhoo.com。

项目还会依赖一些第三方 Python 库,为了方便在服务器上一次性安装,我们将全部依赖写入一个叫 requirements.txt 的文本文件中。激活本地的虚拟环境(如果你使用了虚拟环境的话),并进入项目的根目录,运行 pip freeze > requirements.txt 命令:

(myblog_env) D:Djangoprojectsmyblog>
pip freeze > requirements.txt

这时项目根目录下会生成了一个 requirements.txt 的文本文件,其内容记录了项目的全部依赖。由于有一些依赖包也有先后依赖关系,该调整的需要进去txt文件调整一下。

如果上传到服务器后修改,可以使用 'vi (文件名称)' 打开文件,然后点击键盘 'i' 键切换到输入模式。
修改完需要退出,先按键盘 'Esc' 键,然后输入 ':wq' 保存退出。如果不想修改又无法退出可以用 ':q!' 强制退出,这样不会保存你修改的内容
更多的vi、vim命令使用方法可以自行到搜索引擎搜索学习,这里就不一一讲解了。

将代码上传到 GitHub 、 Gitee 、 或者Coding

将代码上传到 GitHub 等代码托管平台,这样我们就可以方便地把代码拉取到服务器了。
如果访问慢或者打不开GitHub的话,可以上传到国内的Gitee或者Coding

Git的安装和上传代码的简单方法

安装Git可以到官网 https://git-scm.com 下载,安装的话可以一直下一步,完成。

首先你需要一个GitHub 、 Gitee 、 或者Coding账号,所以还没有的话先去注册吧!

https://github.com
https://gitee.com
https://coding.net

1.进入Github首页,点击New repository新建一个项目

2.填写相应信息后点击create即可

Repository name: 仓库名称

Description(可选): 仓库描述介绍

Public, Private : 仓库权限(公开共享,私有或指定合作者)

Initialize this repository with a README: 添加一个README.md

gitignore: 不需要进行版本管理的仓库类型,对应生成文件.gitignore

license: 证书类型,对应生成文件LICENSE

一般可以填写项目仓库名称,点击create就可以了。因为免费用户只有 Gitee 才可以创建私有仓库

3.创建好项目仓库后,在项目仓库页面点击Clone or dowload会出现一个地址,copy这个地址备用。

4.接下来就到本地操作了,首先右键你的项目,如果你之前安装git成功的话,右键会出现两个新选项,分别为Git Gui Here,Git Bash Here,这里我们选择Git Bash Here进入界面,我的本地项目名为myblog。

5.接下来输入如下代码(关键步骤),把github上面的仓库克隆到本地

git clone (粘贴上你之前复制的地址)

6.操作步骤5以后你的本地项目文件夹下面就会多出个文件夹,该文件夹名即为你github上面的项目名,如我多出了个myblogsite文件夹,我们把本地项目文件夹下的所有文件(除了新多出的那个文件夹不用),其余都复制到那个新多出的文件夹下。

7.接着继续输入命令 cd myblogsite,进入myblogsite文件夹

8.接下来依次输入以下代码即可完成其他剩余操作:

git add . (注:别忘记后面的.,此操作是把myblogsite文件夹下面的文件都添加进来)
git commit -m "提交信息" (注:“提交信息”里面换成你需要,如“first commit”)
git push -u origin master (注:此操作目的是把本地仓库push到github上面,此步骤需要你输入GitHub帐号和密码)

注意:数据库文件不要上传!要是SQLLITE3里面有你测试的重要数据,将可能会泄露。

设置服务器目录结构

接下来需要把代码上传到服务器了。我服务器上存放代码的目录结构一般是这样的:

/home/markhoo/
    sites/
        markhoo.com/
            myblog_env/
            myblog/

一台服务器可能部署多个网站,所有网站代码都放在 sites/ 目录下。markhoo.com/ 这个文件夹以网站的域名命名,便于区分。myblog_env 是 python 虚拟环境目录。myblog 是 Django 博客项目目录。
创建这个目录结构,注意目录名替换为你自己的域名,以后涉及到 markhoo.com 的地方通常都要替换你自己的域名,后面就不再一一指出了。
备注:上面的我的目录结构.可能会有新手看不懂,解释一下,就像下面这样一直创建下去

/home/markhoo/sites/markhoo.com/

最后的 myblog_env 和 myblog 是同级目录位于markhoo.com下,这里自己先不要创建。

创建目录的命令为: mkdir (文件夹名称)
删除目录的命令为: rmdir (文件夹名称)

安装virtualenv

为了不影响外界环境的清洁,所以我们使用虚拟环境来配置 Django 项目
首先用xshell登录到你的服务器,然后按一下步骤操作

pip3 install virtualenv

接下来创建虚拟环境,先进入到 markhoo.com 目录下,然后运行 virtualenv 命令创建虚拟环境:

[root@instance-pkb22o0h]# cd /home/markhoo/sites/markhoo.com
[root@instance-pkb22o0h markhoo.com]# virtualenv -p /usr/bin/python3 –-no-site-packages myblog_env

命令说明:
-p: 指定你要虚拟的Python版本,这里选择了本地的python3
–-no-site-packages:表示在建立虚拟环境时不将原版本中的第三方库拷贝过来,这样就能获得一个纯净的Python环境。
myblog_env:表明在该目录下,建立一个叫做myblog_env的虚拟环境,这样的命名方式,让你一眼就能看出这个虚拟环境是为谁建立的。

virtualenv的命令使用方法:
创建虚拟环境: $ virtualenv -p /usr/bin/python3.5 –-no-site-packages myblog_env
启动虚拟环境: $ source myblog/bin/activate 
退出虚拟环境: $ deactivate 
删除虚拟环境: $ rm –r myblog

检查一下虚拟环境是否创建成功,运行 ls 命令列出当前目录下的文件和文件夹,看到 myblog_env 这个文件夹说明虚拟环境创建成功。

[root@instance-pkb22o0h markhoo.com]# ls
myblog_env

venv的使用

由于我只是测试项目,所以使用了Python自带的venv。
先进入到 markhoo.com 目录下,然后运行 python3 -m venv myblog_env 创建虚拟环境:

[root@instance-pkb22o0h]# cd /home/markhoo/sites/markhoo.com
[root@instance-pkb22o0h markhoo.com]# python3 -m venv myblog_env

检查一下虚拟环境是否创建成功,运行 ls 命令列出当前目录下的文件和文件夹,看到 myblog_env 这个文件夹说明虚拟环境创建成功。

[root@instance-pkb22o0h markhoo.com]# ls
myblog_env

在虚拟环境中安装 django 以及依赖包并初始化项目

接着再从代码仓库把项目代码拉取过来,把 git clone 后的地址换成你自己的 GitHub 仓库地址!

[root@instance-pkb22o0h markhoo.com]# git clone https://github.com/MarkHoo/myblog.git

运行 ls 命令检查一下是否拉取成功:

[root@instance-pkb22o0h markhoo.com]# ls
myblog myblog_env

多了 myblog 文件夹(文件夹名称由你的 GitHub 仓库名决定),说明拉取成功了。

接下来执行命令,启动虚拟环境

[root@instance-pkb22o0h markhoo.com]# source myblog_env/bin/activate
(myblog_env) [root@instance-pkb22o0h markhoo.com]#

安装项目环境依赖

(myblog_env) [root@instance-pkb22o0h markhoo.com]# cd myblog
(myblog_env) [root@instance-pkb22o0h myblog]# pip3 install -r requirements.txt

收集静态文件
运行 python manage.py collectstatic 命令收集静态文件到 static 目录下

(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py collectstatic

生成数据库

(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py migrate

创建超级用户

(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py createsuperuser

然后就会出现以下内容,第一个是用户名,第二个是邮箱,第三和第四是密码(输入时没有任何显示,实际上已经输入了)。最后显示创建成功!

Username (leave blank to use 'root'): admin
Email address: admin@markhoo.com
Password:
Password (again):
Superuser created successfully.

备注:这里使用的是Django默认生成的sqlite3数据库,我使用了这个,简单写写博客够用了。

启动 Django

(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py runserver 0.0.0.0:8000

如果没有报错,就说明 Django 已经安装成功了,打开浏览器输入你的服务器IP:8000,就可以看到你的网站了

退出 Django

按 ctrl+c 退出 Django 服务

安装 Mysql (使用MySQL数据库同学可以借鉴)

安装并启动 mariadb

因为 CentOS 7 之后的版本都不在提供 Mysql 安装源,这里我们使用 mariadb 代替 mysql,依次执行下列命令

yum install mariadb mariadb-server -y
yum install MySQL-python -y
systemctl start mariadb

对 mariadb 进行初始化设置

  • 执行下面命令,根据提示操作
  • 设置新密码为 test
  • 默认密码为空,直接回车即可

mysql_secure_installation

使用设置的密码登陆 mariadb

  • 登陆 db,这里假设密码被设置为 admin123

mysql -uroot -ptest

创建一个数据库

create database myblog;

成功后,输入 exit 命令退出 db

exit

修改配置文件,与 Mysql 数据库相关联
编辑 myblog/settings.py

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myblog',
        'PASSWORD':'admin123',
        'USER': 'root',
        'HOST':'127.0.0.1',
        'PORT':'3306',
    }
}

创建 Django 数据库

(myblog_env) [root@instance-pkb22o0h myblog]# cd /home/markhoo/sites/markhoo.com/myblog
(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py migrate

创建超级用户

(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py createsuperuser

然后就会出现以下内容,第一个是用户名,第二个是邮箱,第三和第四是密码(输入时没有任何显示,实际上已经输入了)。最后显示创建成功!

Username (leave blank to use 'root'): admin
Email address: admin@markhoo.com
Password:
Password (again):
Superuser created successfully.

启动 Django

(myblog_env) [root@instance-pkb22o0h myblog]# python manage.py runserver 0.0.0.0:8000

如果没有报错,就说明 Django 已经安装成功了,并且跟 Mysql 的连接正常。打开浏览器输入你的服务器IP:8000,就可以看到你的网站了

退出 Django

按 ctrl+c 退出 Django 服务

使用 Gunicorn

Gunicorn 一般用来管理多个进程,有进程挂了Gunicorn 可以把它拉起来,防止服务器长时间停止服务,还可以动态调整 worker 的数量,请求多的时候增加 worker 的数量,请求少的时候减少。

在虚拟环境下,安装 Gunicorn:

(myblog_env) [root@instance-pkb22o0h myblog]# pip3 install gunicorn

测试Gunicorn是否能启动你的项目服务

(myblog_env) [root@instance-pkb22o0h myblog]# gunicorn --bind 0.0.0.0:8000 myblog.wsgi:application

注意:myblog.wsgi:application这里的myblog对应的是 /home/markhoo/sites/markhoo.com/myblog/myblog, 根据自己的修改。浏览器输入域名,可以看到访问成功了!

访问ip地址看浏览器是否能正常查看内容(此时没有退出虚拟环境)
完成测试后,按CTRL-C 停止 Gunicorn 运行

退出虚拟环境

deactivate

创建一个 Gunicorn Systemd Service 文件,直接运行以下命令

vim /etc/systemd/system/gunicorn.service

修改内容如下:

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=root
Group=nginx
WorkingDirectory=/home/markhoo/sites/markhoo.com/myblog
ExecStart=/home/markhoo/sites/markhoo.com/myblog_env/bin/gunicorn --workers 3 --bind unix:/home/markhoo/sites/markhoo.com/myblog/myblog.sock myblog.wsgi:application

[Install]
WantedBy=multi-user.target

一定要注意自己的项目路径和虚拟环境路径
WorkingDirectory与ExecStart 修改为自己的路径

开启Gunicorn服务并开机自启,运行以下命令

systemctl start gunicorn
systemctl enable gunicorn

配置 Nginx

接下是配置 Nginx 来处理用户请求

配置nginx代理通过Gunicorn
依次运行以下命令

(myblog_env) [root@instance-pkb22o0h myblog]# cd /etc/nginx/
(myblog_env) [root@instance-pkb22o0h nginx]# ls
conf.d fastcgi.conf fastcgi_params koi-utf mime.types nginx.conf scgi_params uwsgi
default.d fastcgi.conf.default fastcgi_params.default koi-win mime.types.default nginx.conf.default scgi_params.default uwsgi
(myblog_env) [root@instance-pkb22o0h nginx]# vi nginx.conf

或者直接运行

vim /etc/nginx/nginx.conf

打开后默认的内容如下:

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2 default_server;
#        listen       [::]:443 ssl http2 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers HIGH:!aNULL:!MD5;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        location / {
#        }
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

}

找到server内容进行修改,具体内容如下:

server {
    listen 80;
    server_name  markhoo.com 0.0.0.0;

        location = /favicon.ico { access_log off; log_not_found off; }
        location /static/ {
        root /home/markhoo/sites/markhoo.com/myblog;
        }

        location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/home/markhoo/sites/markhoo.com/myblog/myblog.sock;
        }

    }

注意修改自己的IP地址或域名,还有文件路径
server_domain_or_IP 代表你的IP地址或域名

  • 这里的域名为 markhoo.com,其次一定要添加 0.0.0.0 否则无法访问,两个之间用空格隔开。
  • 所有URL 带有 /static 的请求均由 Nginx 处理,alias 指明了静态文件的存放目录。
  • 其它请求转发给 Django 处理。proxy_pass 后面使用了 unix 套接字,其作用是防止端口冲突,这里就不再详述。

修改所涉及到的vim命令使用方法:使用 'vi (文件名称)' 是打开文件,然后点击键盘 'i' 键切换到输入模式。修改完需要退出vim编辑器,先按键盘 'Esc' 键,然后输入 ':wq' 保存退出。如果不想修改又无法退出可以用 ':q!' 强制退出,这样不会保存你修改的内容。
更多的vi、vim命令的具体使用方法可以自行到搜索引擎搜索学习,这里也不一一讲解了。

修改nginx的权限

usermod -a -G root nginx
chmod 710 /home/
nginx -t

如果没有报错,就行下一步操作

开启nginx服务并开机自启

systemctl start nginx
systemctl enable nginx

现在,一切配置完成!你可以访问你的域名了!

于6.20号写的文章相呼应。
新blog主要部分于2018.8.6开发完成,并于今日晚上成功上线测试【http://test.markhoo.com】,部署在百度云服务器。具体上线部署细节已用sublime记录700多行,等整理好发布分享。
最后,欢迎大家访问体验并找bug,提出建议。

官网太慢甚至打不开,所以干脆转载转来,方便学习。

|xadmin| 插件制作

插件原理

|xadmin| 的插件系统架构设计一定程度上借鉴了 wordpress 的设计。 想要了解 |xadmin| 的插件系统架构首先需要了解 |xadmin| AdminView 的概念。
简单来说,就是 |xadmin| 系统中每一个页面都是一个 AdminView 对象返回的 HttpResponse 结果。|xadmin| 的插件系统做的事情其实就是在 AdminView
运行过程中改变其执行的逻辑, 或是改变其返回的结果,起到修改或增强原有功能的效果。下面让我们看看整个插件从制作完成到实际运行的整个过程。

首先需要创建自己的插件类, 插件类继承 :class:~xadmin.views.BaseAdminPlugin ::


class HelloWorldPlugin(BaseAdminPlugin):
    ...

开发好的插件首先要注册到 |xadmin| 中, 示例代码如下::


# ListAdminView 是 Model 列表页面
xadmin.site.register_plugin(HelloWorldPlugin, ListAdminView)

其中插件的注册和使用可以参看 :meth:xadmin.sites.AdminSite.register_plugin

当将插件注册到 |xadmin| 后, |xadmin| 在创建 AdminView 实例的时候会将该插件放入实例的 :attr:plugins 属性。当 AdminView 在处理请求
时,会首先逐个调用 :attr:plugins 中插件的 :meth:~xadmin.views.BaseAdminPlugin.init_request 方法,插件在该方法中一般进行初始化的操作并且返回一个 Boolean 值告诉 AdminView
是否需要加载该插件。当 :meth:~xadmin.views.BaseAdminPlugin.init_request 方法返回值为 False 时, AdminView 不会加载该插件。实例如下::


class HelloWorldPlugin(BaseAdminPlugin):
    say_hello = False
    # 初始化方法根据 ``say_hello`` 属性值返回
    def init_request(self, *args, **kwargs):
        return bool(self.say_hello)

在以上实例中,插件根据自身的 say_hello 属性来决定是否让自己被加载。您可能会迷惑, say_hello 属性看起来一直会是 False 呀,那样这个插件不是永远不会被加载?
其实 |xadmin| 在创建插件实例的时候会将 OptionClass 的同名属性替换插件的属性。这样,在不同的 OptionClass 下会有不同的插件结果,实例如下::


class SomeModelAdmin(object):
    say_hello = True
    ...
site.register(SomeModel, SomeModelAdmin)

理解以上内容后,让我们再看看插件具体是如何起作用的。在 AdminView 的执行过程中,可以被插件截获或修改的方法使用 :func:~xadmin.views.base.filter_hook 装饰,实例如下::

class ListAdminView(ModelAdminView):
    # 可以被插件截获或修改的方法使用该装饰器装饰
    @filter_hook
    def get_context(self):
        ...

使用 :func:~xadmin.views.base.filter_hook 装饰的方法执行过程中会根据一定原则执行插件中的同名方法,具体信息查考该装饰器的文档内容。

.. autofunction:: xadmin.views.base.filter_hook

根据该装饰器的执行原则,如果我们想修改上面示例中 ListAdminViewget_context 的返回值,可以在插件中实现如下代码::


class HelloWorldPlugin(BaseAdminPlugin):
    # 在插件中加入同名方法,修改 ``ListAdminView`` 的 ``get_context`` 返回的值
    def get_context(self, context):
        context.update({'hello_target': 'World!!'})
        return context

如果我们希望插件在 AdminView 的方法前执行,或是完全使用自己的方法替代 AdminView 的方法可以这样::


class HelloWorldPlugin(BaseAdminPlugin):
    # 第一个参数为 ``__`` 。这样 ``__`` 即为 ``ListAdminView`` 的 ``get_context`` 方法本身,注意,这时还没有执行这个方法。
    def get_context(self, __):
        context = {'hello_target': 'World!!'}
        #我们可以在任何时候执行 ``AdminView`` 的方法,或是根本不执行
        context.update(__())
        return context

至此,加入的插件就实现了对 AdminView 方法的完全控制。

模板插件

我们知道,Django 中一个完整的 View 是包含模板的,模板用来生成 View 最终返回的 HTML 内容。当然,插件也可以在模板中插入自己的内容。我们来看看具体如何实现。

首先让我们来看看 |xadmin| 中的模板代码示例片段 (change_list.html):

.. code-block:: html


{% load xadmin %}
...
<form id="changelist-form" action="" method="post"{% view_block 'result_list_form' %}>{% csrf_token %}
  {% view_block 'results_top' %}
  <div class="results">
    {% if results %}
    ...

其中的 view_block Tag 即为插件的 插入点 。插件可以在自己的插件类中使用 block_ + 插入点名称 方法将 HTML 片段插入到页面的这个位置,示例如下:

.. code-block:: python


class HelloWorldPlugin(BaseAdminPlugin):
    
    # context 即为 TemplateContext, nodes 参数包含了其他插件的返回内容。
    # 您可以直接返回 HTML 片段,或是将内容加入到 nodes 参数中
    def block_results_top(self, context, nodes):
        return s"<div class='info'>Hello %s</div>" % context['hello_target']

插件实例

下面让我们来看一个 |xadmin| 中完整的插件实例::


from django.template import loader

from xadmin.sites import site
from xadmin.views import BaseAdminPlugin, ListAdminView

REFRESH_VAR = '_refresh'

# 该插件实现了一个列表页面刷新器的效果
class RefreshPlugin(BaseAdminPlugin):

    # 用户可以定制刷新的频率,可以传入多个值。该属性会被 ``OptionClass`` 的同名属性替换
    refresh_times = []
    
    def init_request(self, *args, **kwargs):
        # 根据用户是否制定了刷新器来决定是否启动该插件
        return bool(self.refresh_times)

    # 插件拦截了返回 Media 的方法,加入自己需要的 js 文件。
    def get_media(self, media):
        if self.request.GET.get(REFRESH_VAR):
            # 放页面处于自动刷新状态时,加入自己的 js 制定刷新逻辑
            media.add_js([self.static('xadmin/js/refresh.js')])
        return media

    # Block Views
    # 在页面中插入 HTML 片段,显示刷新选项。
    def block_top_toolbar(self, context, nodes):
        current_refresh = self.request.GET.get(REFRESH_VAR)
        context.update({
            'has_refresh': bool(current_refresh),
            'clean_refresh_url': self.admin_view.get_query_string(remove=(REFRESH_VAR,)),
            'current_refresh': current_refresh,
            'refresh_times': [{
                'time': r,
                'url': self.admin_view.get_query_string({REFRESH_VAR: r}),
                'selected': str(r) == current_refresh,
            } for r in self.refresh_times],
        })
        # 可以将 HTML 片段加入 nodes 参数中
        nodes.append(loader.render_to_string('xadmin/blocks/refresh.html', context_instance=context))
# 注册插件
site.register_plugin(RefreshPlugin, ListAdminView)

最后不要忘记在适当的地方加载该代码, 让其执行。一般情况下,你可以将其写到 adminx.py 文件中,这样,只要您的 APP 加入到 Django Settings 的 INSTALL_APPS 中,
|xadmin| 就会自动执行 app 下的 adminx.py 文件。

插件开发

了解了插件的运行原理后我们就可以开发自己的插件了。首先我们需要了解插件类中的实用方法。因为插件是继承 :class:~xadmin.views.BaseAdminPlugin 类,而该类继承自
:class:~xadmin.views.BaseAdminObject,所以这两个类的方法都可以在插件中使用。

|xadmin| 在创建插件时会自动注入以下属性到插件实例中:


* request : Http Request

* user : 当前 User 对象

* args : View 方法的 args 参数

* kwargs : View 方法的 kwargs 参数

* admin_view : ``AdminView`` 实例

* admin_site : |xadmin| 的 ``admin_site`` 对象实例

如果 AdminView 是 :class:~xadmin.views.ModelAdminView 的子类,还会自动注入以下属性:


* model : Model 对象

* opts : Model 的 _meta 属性

接下来您应该考虑打算制作什么功能的插件了。不同功能的插件额能需要注册到不同的 AdminView 上,|xadmin| 系统中
主要的 AdminView 有:


* :class:`~xadmin.views.BaseAdminView` : 所有 ``AdminView`` 的基础类,注册在该 View 上的插件可以影响所有的 ``AdminView``

* :class:`~xadmin.views.CommAdminView` : 用户已经登陆后显示的 View,也是所有登陆后 View 的基础类。该 View主要作用是创建了 |xadmin| 的通用元素,例如:系统菜单,用户信息等。插件可以通过注册该 View 来修改这些信息。

* :class:`~xadmin.views.ModelAdminView` : 基于 Model 的 ``AdminView`` 的基础类,注册的插件可以影响所有基于 Model 的 View。

* :class:`~xadmin.views.ListAdminView` : Model 列表页面 View。

* :class:`~xadmin.views.ModelFormAdminView` : Model 编辑页面 View。

* :class:`~xadmin.views.CreateAdminView` : Model 创建页面 View。

* :class:`~xadmin.views.UpdateAdminView` : Model 修改页面 View。

* :class:`~xadmin.views.DeleteAdminView` : Model 删除页面 View。

* :class:`~xadmin.views.DetailAdminView` : Model 详情页面 View。

选择好目标 AdminView 后就要在自己的插件中编写方法来修改或增强这些 AdminView 。其中每个 AdminView 可以
拦截的方法及其介绍请参看各 AdminView 的文档。

插件规范

文档模板::

"""
Name
======

作者
----

该插件的作者信息

功能
----

描述插件的主要功能

截图
----

.. image:: /images/plugins/action.png

使用
----

描述插件的使用方法,  以及使用示例.

版本
----

描述插件的版本信息

API
---
.. autoclass:: XXX

"""