Django Xadmin 官方文档 之五 Xadmin 插件制作

欢迎大家扫码关注我的微信公众号:
数据之恋

一、插件原理

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

首先需要创建自己的插件类, 插件类继承 BaseAdminPlugin

class HelloWorldPlugin(BaseAdminPlugin):
    ...

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

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

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

当将插件注册到 Xadmin 后, Xadmin 在创建 AdminView实例的时候会将该插件放入实例的 plugins属性。 当 AdminView在处理请求 时, 会首先逐个调用 plugins中插件的 init_request()方法, 插件在该方法中一般进行初始化的操作并且返回一个 Boolean 值告诉 AdminView是否需要加载该插件。 当 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 ListAdminView(ModelAdminView):
    # 可以被插件截获或修改的方法使用该装饰器装饰
    @filter_hook
    def get_context(self):
        ...

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

xadmin.views.base.filter_hook(func)

表明 AdminView 的方法可以被插件插入的装饰器。 执行使用了该装饰器的方法时, 会按照以下过程执行:

1. 首先将实例的 plugins 属性取出, 取出含有同样方法名的插件;

2. 按照插件方法的 priority 属性排序;

3. 顺序执行插件方法, 执行插件方法的规则:

如果插件方法没有参数,AdminView 方法的返回结果不为空则抛出异常;

如果插件方法的第一个参数为 __ , 则 AdminView 方法将作为第一个参数传入, 注意, 这时还未执行该方法, 在插件中可以通过 __() 执行, 这样就可以实现插件在 AdminView 方法执行前实现一些自己的逻辑, 例如:

def get_context(self, __):
    c = {'key': 'value'}
    c.update(__())
    return c

如果插件方法的第一个参数不为 __ , 则执行 AdminView 方法, 将结果作为第一个参数传入;

4. 最终将插件顺序执行的结果返回;

根据该装饰器的执行原则, 如果我们想修改上面示例中 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):

{% 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 片段插入到页面的这个位置, 示例如下:

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 文件。

三、插件开发

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

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

  • request: Http Request
  • user: 当前 User 对象
  • args: View 方法的 args 参数
  • kwargs: View 方法的 kwargs 参数
  • admin_view: AdminView 实例
  • admin_site: Xadmin 的 admin_site 对象实例

如果 AdminViewModelAdminView的子类, 还会自动注入以下属性:

  • model: Model 对象
  • opts: Model 的 _meta 属性

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

  • BaseAdminView: 所有 AdminView的基础类, 注册在该 View 上的插件可以影响所有的 AdminView
  • CommAdminView: 用户已经登录后显示的 View, 也是所有登录后 View 的基础类。 该 View主要作用是创建了 Xadmin 的通用元素, 例如: 系统菜单、 用户信息等。 插件可以通过注册该 View 来修改这些信息;
  • ModelAdminView: 基于 Model 的 AdminView的基础类,注册的插件可以影响所有基于 Model 的 View;
  • ListAdminView: Model 列表页面 View;
  • ModelFormAdminView: Model 编辑页面 View;
  • CreateAdminView: Model 创建页面 View;
  • UpdateAdminView: Model 修改页面 View;
  • DeleteAdminView: Model 删除页面 View;
  • DetailAdminView: Model 详情页面 View;

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

四、插件规范

文档模板:

"""
Name
======

作者
----

该插件的作者信息

功能
----

描述插件的主要功能

截图
----

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

使用
----

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

版本
----

描述插件的版本信息

API
---
.. autoclass:: XXX

"""

【“插件规范” 应该是官网为了规范开发者制作插件的格式而统一制定的。 不知道理解的对不对, 希望能得到大神的指点。】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值