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

"""

【“插件規範” 應該是官網爲了規範開發者製作插件的格式而統一制定的。 不知道理解的對不對, 希望能得到大神的指點。】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章