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. 最终将插件顺序执行的结果返回;
根据该装饰器的执行原则, 如果我们想修改上面示例中 ListAdminView
的 get_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 %}
...