Horizon二次開發

注:OpenStack Horizon項目與其它項目有所不同,它主要提供一套工具,我們可以自己定製開發我們想要的dashboard(控制面板)。

翻譯自:http://docs.openstack.org/developer/horizon/topics/tutorial.html

本教程討論如何使用Horizon中多樣的組件(主要是tables和tabs)來建立一個dashboard和panel。


創建一個dashboard控制面板

注意:你可以創建一個panel而不是dashboard,然後整合它到一個已經存在的dashboard。

快速版本

Horizon提供了一套自定義管理命令來創建一個典型的基於dashboard的結構。下列命令產生了大部分的模板代碼:

./run_tests.sh -m sstartdash mydashboard
非常推薦你閱讀剩餘的章節來了解這個命令創建了什麼和爲什麼要創建。

結構

dashboard(或者panel)的推薦的結構適合於典型的Django應用佈局,我們將我們的dashboard命名爲My Dashboard”:

mydashboard
  |--__init__.py
  |--dashboard.py
  |--templates/
  |--static/
dashboard.py模塊將通過使用Horizon包含我們要使用的dashboard類;templates和static目錄分別是我們的Django模板文件和靜態媒體文件。

在static和templates目錄中,不錯的命名空間應該像這樣:

templates/
  |--mydashboard/
static/
  |--mydashboard/
     |--css/
     |--js/
     |--img/
在那些文件和目錄的地方,我們可以繼續寫我們自己的dashboard類。

定義一個dashboard

一個dashboard可以極其簡單,至少定義一個name(dashboard對外所呈現的名稱)和一個slug(被其它組件引用的內部名稱):

import horizon
 
class MyDashboard(horizon.Dashboard):
    name = _("My Dashboard")
    slug = "mydashboard"

在實踐中,一個dashboard類通常包括更多信息,例如一系列的panels和任何訪問dashboard的權限:

class MyDashboard(horizon.Dashboard):
    name = _("My Dashboard")
    slug = "mydashboard"
    panels = ('mypanel',)
    default_panel = 'mypanel'
    permissions = ('openstack.roles.admin'<span style="font-size:12px;">,)</span>

一旦我們的dashboard完成,我們需要做的就是註冊它:

horizon.register(MyDashboard)
這個操作的典型的位置是dashboard.py文件的底部,但是它可以在任何的其他地方,例如在一個重寫的文件中。


創建一個panel

現在我們已經有了我們寫的dashboard,我們也可以創建我們的panel,我們將panel命名爲“My Panel”。

注意:你不需要爲了添加panel而去寫一個dashboard,這個結構是爲了保證教程的完整性。

快速版本

Horizon提供了一套自定義的管理命令來創建一個典型的基於panel的結構。下面的命令生成了大部分的模板代碼:

./run_test.sh -m startpanel mypanel --dashboard=mydashboard --target=auto
dashboard參數是必選的,告訴命令panel註冊的dashboard。target參數是可選的,auto意味着創建的panel文件應該在dashboard模塊內相對於當前的目錄(默認)。

非常推薦你閱讀剩餘的章節來了解這個命令創建了什麼和爲什麼要創建。

結構

一個Panel是一個相對扁平的結構,繼續我們的mydashboard/mypanel的例子,它們看起來應該是這樣:

mypanel/
  |--__init__.py
  |--panel.py
  |--urls.py
  |--views.py
  |--templates/
     |--mypanel/
        |--index.html

定義一個panel

上面指定的panel.py文件有一個特殊的意義,在一個dashboard中,dashboard類中指定的"panels屬性"列出任何模塊名字,將自動在panel.py文件中查找相應的記錄。

在panel.py模塊中,我們定義我們的panel類:

class MyPanel(horizon.Panel):
    name = _("My Panel")
    slug = 'mypanel'

一旦定義了它,我們還需要在dashboard中註冊它,通常在panel.py文件末尾:

dashboard.VizDash.register(MyPanel)

從之前構建的例子,我們可能想要定義一組panels共享主題,而且在導航上有一個子標題,我們將我們的panels組命名爲“My Group”:

class MyGroup(horizon.PanelGroup):
    slug = "mygroup"
    name = _("My Group")
    panels = ('mypanel',)
PanelGroup可以添加到dashboard類的panels列表。


Tables,Tabs,Views

我們從table開始,把table和tab整合到一起,最後把它們統一合成到一個視圖(view)中。

從高層次視圖開始,我們最終的目標是創建一個視圖(我們的IndexViex類),使用Horizon的DataTable類來展示數據,使用Horizon的TabGroup類來提供給用戶一個分頁式的展示方式。

我們先從table開始,整合它與tab,然後建立我們的視圖。

定義一個table

Horizon提供了一個DataTable類,簡化了絕大多數顯示數據給最終用戶的工作。這種情況下,我們使用table呈現數據,所以開始定義我們的table,一個table.py文件:

from django.utils.translation import ugettext_lazy as _
from horizon import tables

class InstancesTable(tables.DataTable):
    name = tables.Column("name",verbose_name=_("Name"))
    status = tables.Column("status",verbose_name=_("Status")
    zone = tables.Column('availability_zone',verbose_name=_("Availability Zone"))
    image_name = tables.Column('image_name',verbose_name=_("Image Name")

class Meta: 
    name = "instances"
    verbose_name = _("Instances")

有幾件事情發生了,我們創建了一個table子類,定義了四列。每列定義了它訪問實例類的屬性作爲第一個參數,由於我們希望所有的事情都是可以被翻譯的,我們給沒列一個verbose_name標記它們可以被翻譯。最後我們添加了一個Meta類來定義一些描述table的元數據。

向table添加action

Horizon提供了三種基本的可以呈現table數據的action類:

在基本的action的基礎上,還提供了一些擴展的action類:

現在,我們創建並向table添加一個“filter action”。我們需要編輯上面的table.py文件,添加了“filter action”後,我們將只能看到filter域中規定的字段。先定義一個FilterAction類:

class MyFilterAction(tables.FilterAction):
    name = "myfilter"
然後,我們將這個action加入到table中:

class InstancesTable:
    class Meta:
        table_actions = (MyFilterAction,)

完整的table.py文件看起來應該是這樣:

from django.utils.translation import ugettext_lazy as _

from horizon import tables


class MyFilterAction(tables.FilterAction):
    name = "myfilter"


class InstancesTable(tables.DataTable):
    name = tables.Column('name', \
                         verbose_name=_("Name"))
    status = tables.Column('status', \
                           verbose_name=_("Status"))
    zone = tables.Column('availability_zone', \
                         verbose_name=_("Availability Zone"))
    image_name = tables.Column('image_name', \
                               verbose_name=_("Image Name"))

    class Meta:
        name = "instances"
        verbose_name = _("Instances")
        table_actions = (MyFilterAction,)

定義tabs

因爲我們有了table,可以接收數據,我們可以直接得到一個視圖,這種情況下,我們同樣適用Horizon的TabGroup類,它給我們一個乾淨、簡化的tabs接口來顯示我們的可視化數據。

整合table與tab:

from django.utils.translation import ugettext_lazy as _

from horizon import exceptions
from horizon import tabs

from openstack_dashboard import api
from openstack_dashboard.dashboards.mydashboard.mypanel import tables


class InstanceTab(tabs.TableTab):
    name = _("Instances Tab")
    slug = "instances_tab"
    table_classes = (tables.InstancesTable,)
    template_name =  ("horizon/common/_detail_table.html")
    preload = False

    def has_more_data(self, table):
        return self._has_more

    def get_instances_data(self):
        try:
            marker = self.request.GET.get(
                        tables.InstancesTable._meta.pagination_param, None)

            instances,self._has_more = api.nova.server_list(
                self.request,
                search_opts = {'marker': marker,'paginate': True})

            return instances
        except Exception:
            self._has_more = False
            error_message = _('Unable to get instances'>)
            exceptions.handle(self.request, error_message)

            return []

class MypanelTabs(tabs.TabGroup):
    slug = "mypanel_tabs"
    tabs = (InstanceTab,)
    sticky = True


這個tab有一點複雜。tab處理tables的數據(以及所有有關的特性),同時它可以使用preload屬性來指定這個tab該不該被加載,默認情況下爲不加載。當有人點擊它時,它將通過AJAX方式加載,在絕大多數情況下保存我們的API調用。

把他們整合到一個視圖(view)中

在Horizon中,有很多基於類的預建視圖,我們試着給所有通用整合組件提供起始點。

這種情況我們想要一個起始的視圖類型與tables和tabs一起工作,那應該是TabbedTableView類。它很好的動態延遲加載能力(tab group提供的並混合在actions中)和AJAX方式更新tables的能力,使得在用戶端基本不用做任何工作。

代碼看起來應該是這樣:

from horizon import tabs

from openstack_dashboard.dashboards.mydashboard.mypanel \
    import tabs as mydashboard_tabs

class IndexView(tabs.TabbedTableView):
    tab_group_class = mydashboard_tabs.MypanelTabs
    template_name = 'mydashboard/mypanel/index.html'

    def get_data(self, request, context, *args, **kwargs):
        # Add data to the context here...
        return context


URLs

一個充滿智慧的設想就是Panel類可以在url.py文件中被找到,我們定義一個叫做index的視圖作爲panel的默認處理視圖,一個完整的url.py文件看起來應該是這樣:

from django.conf.urls import patterns
from django.conf.urls import url

from openstack_dashboard.dashboards.mydashboard.mypanel import views


urlpatterns = patterns('',
    url(r'^$',
        views.IndexView.as_view(), name='index'),
)


Template(模板)

{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Mypanel" %}{% endblock %}

{% block page_header %}
    {% include "horizon/common/_page_header.html" with title=_("Mypanel") %}
{% endblock page_header %}

{% block main %}
<pre><div class="row">
   <div class="col-sm-12">
   {{ tab_group.render }}
   </div>
</div>
{% endblock %}


如果你想更改index.html的title,你可以更改{% block title %}塊中的內容。

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