Sophie

Sophie

distrib > Fedora > 18 > i386 > by-pkgid > 6f61f4fc58119d17ef9d99939eb417b3 > files > 864

python-django-horizon-doc-2012.2.3-1.fc18.noarch.rpm


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>Building on Horizon &mdash; Horizon 2012.2.3 documentation</title>
    
    <link rel="stylesheet" href="../_static/nature.css" type="text/css" />
    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
    <link rel="stylesheet" href="../_static/tweaks.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    '../',
        VERSION:     '2012.2.3',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="../_static/jquery.js"></script>
    <script type="text/javascript" src="../_static/underscore.js"></script>
    <script type="text/javascript" src="../_static/doctools.js"></script>
    <script type="text/javascript" src="../_static/jquery.tweet.js"></script>
    <link rel="top" title="Horizon 2012.2.3 documentation" href="../index.html" />
    <link rel="next" title="Deploying Horizon" href="deployment.html" />
    <link rel="prev" title="Horizon Quickstart" href="../quickstart.html" /> 
  </head>
  <body>
  <div id="header">
    <h1 id="logo"><a href="http://www.openstack.org/">OpenStack</a></h1>
    <ul id="navigation">
      <li><a href="http://www.openstack.org/" title="Go to the Home page" class="link">Home</a></li>
      <li><a href="http://www.openstack.org/projects/" title="Go to the OpenStack Projects page">Projects</a></li>
      <li><a href="http://www.openstack.org/user-stories/" title="Go to the User Stories page" class="link">User Stories</a></li>
      <li><a href="http://www.openstack.org/community/" title="Go to the Community page" class="link">Community</a></li>
      <li><a href="http://www.openstack.org/blog/" title="Go to the OpenStack Blog">Blog</a></li>
      <li><a href="http://wiki.openstack.org/" title="Go to the OpenStack Wiki">Wiki</a></li>
      <li><a href="http://docs.openstack.org/" title="Go to OpenStack Documentation" class="current">Documentation</a></li>
    </ul>
  </div>
  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="building-on-horizon">
<h1>Building on Horizon<a class="headerlink" href="#building-on-horizon" title="Permalink to this headline">¶</a></h1>
<p>This tutorial covers how to use the various components in Horizon to build
an example dashboard and panel with a data table and tabs.</p>
<p>As an example, we&#8217;ll build on the Nova instances API to create a new and novel
&#8220;visualizations&#8221; dashboard with a &#8220;flocking&#8221; panel that presents the instance
data in a different manner.</p>
<p>You can find a reference implementation of the code being described here
on github at <a class="reference external" href="https://github.com/gabrielhurley/horizon_demo">https://github.com/gabrielhurley/horizon_demo</a>.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">There are a variety of other resources which may be helpful to read first,
since this is a more advanced tutorial. For example, you may want to start
with the <a class="reference internal" href="../quickstart.html"><em>Horizon quickstart guide</em></a> or the
<a class="reference external" href="https://docs.djangoproject.com/en/1.4/intro/tutorial01/">Django tutorial</a>.</p>
</div>
<div class="section" id="creating-a-dashboard">
<h2>Creating a dashboard<a class="headerlink" href="#creating-a-dashboard" title="Permalink to this headline">¶</a></h2>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">It is perfectly valid to create a panel without a dashboard, and
incorporate it into an existing dashboard. See the section
<a class="reference internal" href="#overrides"><em>overrides</em></a> later in this document.</p>
</div>
<div class="section" id="the-quick-version">
<h3>The quick version<a class="headerlink" href="#the-quick-version" title="Permalink to this headline">¶</a></h3>
<p>Horizon provides a custom management command to create a typical base
dashboard structure for you. The following command generates most of the
boilerplate code explained below:</p>
<div class="highlight-python"><pre>./run_tests.sh -m startdash visualizations</pre>
</div>
<p>It&#8217;s still recommended that you read the rest of this section to understand
what that command creates and why.</p>
</div>
<div class="section" id="structure">
<h3>Structure<a class="headerlink" href="#structure" title="Permalink to this headline">¶</a></h3>
<p>The recommended structure for a dashboard (or panel) follows suit with the
typical Django application layout. We&#8217;ll name our dashboard &#8220;visualizations&#8221;:</p>
<div class="highlight-python"><pre>visualizations
  |--__init__.py
  |--dashboard.py
  |--templates/
  |--static/</pre>
</div>
<p>The <tt class="docutils literal"><span class="pre">dashboard.py</span></tt> module will contain our dashboard class for use by
Horizon; the <tt class="docutils literal"><span class="pre">templates</span></tt> and <tt class="docutils literal"><span class="pre">static</span></tt> directories give us homes for our
Django template files and static media respectively.</p>
<p>Within the <tt class="docutils literal"><span class="pre">static</span></tt> and <tt class="docutils literal"><span class="pre">templates</span></tt> directories it&#8217;s generally good to
namespace your files like so:</p>
<div class="highlight-python"><pre>templates/
  |--visualizations/
static/
  |--visualizations/
     |--css/
     |--js/
     |--img/</pre>
</div>
<p>With those files and directories in place, we can move on to writing our
dashboard class.</p>
</div>
<div class="section" id="defining-a-dashboard">
<h3>Defining a dashboard<a class="headerlink" href="#defining-a-dashboard" title="Permalink to this headline">¶</a></h3>
<p>A dashboard class can be incredibly simple (about 3 lines at minimum),
defining nothing more than a name and a slug:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">horizon</span>

<span class="k">class</span> <span class="nc">VizDash</span><span class="p">(</span><span class="n">horizon</span><span class="o">.</span><span class="n">Dashboard</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Visualizations&quot;</span><span class="p">)</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&quot;visualizations&quot;</span>
</pre></div>
</div>
<p>In practice, a dashboard class will usually contain more information, such as a
list of panels, which panel is the default, and any permissions required to
access this dashboard:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">VizDash</span><span class="p">(</span><span class="n">horizon</span><span class="o">.</span><span class="n">Dashboard</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Visualizations&quot;</span><span class="p">)</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&quot;visualizations&quot;</span>
    <span class="n">panels</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;flocking&#39;</span><span class="p">,)</span>
    <span class="n">default_panel</span> <span class="o">=</span> <span class="s">&#39;flocking&#39;</span>
    <span class="n">permissions</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;openstack.roles.admin&#39;</span><span class="p">,)</span>
</pre></div>
</div>
<p>Building from that previous example we may also want to define a grouping of
panels which share a common theme and have a sub-heading in the navigation:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">InstanceVisualizations</span><span class="p">(</span><span class="n">horizon</span><span class="o">.</span><span class="n">PanelGroup</span><span class="p">):</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&quot;instance_visualizations&quot;</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Instance Visualizations&quot;</span><span class="p">)</span>
    <span class="n">panels</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;flocking&#39;</span><span class="p">,)</span>


<span class="k">class</span> <span class="nc">VizDash</span><span class="p">(</span><span class="n">horizon</span><span class="o">.</span><span class="n">Dashboard</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Visualizations&quot;</span><span class="p">)</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&quot;visualizations&quot;</span>
    <span class="n">panels</span> <span class="o">=</span> <span class="p">(</span><span class="n">InstanceVisualizations</span><span class="p">,)</span>
    <span class="n">default_panel</span> <span class="o">=</span> <span class="s">&#39;flocking&#39;</span>
    <span class="n">permissions</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;openstack.roles.admin&#39;</span><span class="p">,)</span>
</pre></div>
</div>
<p>The <tt class="docutils literal"><span class="pre">PanelGroup</span></tt> can be added to the dashboard class&#8217; <tt class="docutils literal"><span class="pre">panels</span></tt> list
just like the slug of the panel can.</p>
<p>Once our dashboard class is complete, all we need to do is register it:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">horizon</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">VizDash</span><span class="p">)</span>
</pre></div>
</div>
<p>The typical place for that would be the bottom of the <tt class="docutils literal"><span class="pre">dashboard.py</span></tt> file,
but it could also go elsewhere, such as in an override file (see below).</p>
</div>
</div>
<div class="section" id="creating-a-panel">
<h2>Creating a panel<a class="headerlink" href="#creating-a-panel" title="Permalink to this headline">¶</a></h2>
<p>Now that we have our dashboard written, we can also create our panel. We&#8217;ll
call it &#8220;flocking&#8221;.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">You don&#8217;t need to write a custom dashboard to add a panel. The structure
here is for the sake of completeness in the tutorial.</p>
</div>
<div class="section" id="id1">
<h3>The quick version<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
<p>Horizon provides a custom management command to create a typical base
panel structure for you. The following command generates most of the
boilerplate code explained below:</p>
<div class="highlight-python"><pre>./run_tests.sh -m startpanel flocking --dashboard=visualizations --target=auto</pre>
</div>
<p>The <tt class="docutils literal"><span class="pre">dashboard</span></tt> argument is required, and tells the command which dashboard
this panel will be registered with. The <tt class="docutils literal"><span class="pre">target</span></tt> argument is optional, and
respects <tt class="docutils literal"><span class="pre">auto</span></tt> as a special value which means that the files for the panel
should be created inside the dashboard module as opposed to the current
directory (the default).</p>
<p>It&#8217;s still recommended that you read the rest of this section to understand
what that command creates and why.</p>
</div>
<div class="section" id="id2">
<h3>Structure<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
<p>A panel is a relatively flat structure with the exception that templates
for a panel in a dashboard live in the dashboard&#8217;s <tt class="docutils literal"><span class="pre">templates</span></tt> directory
rather than in the panel&#8217;s <tt class="docutils literal"><span class="pre">templates</span></tt> directory. Continuing our
vizulaization/flocking example, let&#8217;s see what the looks like:</p>
<div class="highlight-python"><pre># stand-alone panel structure
flocking/
  |--__init__.py
  |--panel.py
  |--urls.py
  |--views.py
  |--templates/
     |--flocking/
        |--index.html

# panel-in-a-dashboard structure
visualizations/
|--__init__.py
|--dashboard.py
|--flocking/
   |--__init__.py
   |--panel.py
   |--urls.py
   |--views.py
|--templates/
   |--visualizations/
      |--flocking/
         |--index.html</pre>
</div>
<p>That follows standard Django namespacing conventions for apps and submodules
within apps. It also works cleanly with Django&#8217;s automatic template discovery
in both cases.</p>
</div>
<div class="section" id="defining-a-panel">
<h3>Defining a panel<a class="headerlink" href="#defining-a-panel" title="Permalink to this headline">¶</a></h3>
<p>The <tt class="docutils literal"><span class="pre">panel.py</span></tt> file referenced above has a special meaning. Within a
dashboard, any module name listed in the <tt class="docutils literal"><span class="pre">panels</span></tt> attribute on the
dashboard class will be auto-discovered by looking for <tt class="docutils literal"><span class="pre">panel.py</span></tt> file
in a corresponding directory (the details are a bit magical, but have been
thoroughly vetted in Django&#8217;s admin codebase).</p>
<p>Inside the <tt class="docutils literal"><span class="pre">panel.py</span></tt> module we define our <tt class="docutils literal"><span class="pre">Panel</span></tt> class:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Flocking</span><span class="p">(</span><span class="n">horizon</span><span class="o">.</span><span class="n">Panel</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Flocking&quot;</span><span class="p">)</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&#39;flocking&#39;</span>
</pre></div>
</div>
<p>Simple, right? Once we&#8217;ve defined it, we register it with the dashboard:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">visualizations</span> <span class="kn">import</span> <span class="n">dashboard</span>

<span class="n">dashboard</span><span class="o">.</span><span class="n">VizDash</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">Flocking</span><span class="p">)</span>
</pre></div>
</div>
<p>Easy! There are more options you can set to customize the <tt class="docutils literal"><span class="pre">Panel</span></tt> class, but
it makes some intelligent guesses about what the defaults should be.</p>
</div>
<div class="section" id="urls">
<h3>URLs<a class="headerlink" href="#urls" title="Permalink to this headline">¶</a></h3>
<p>One of the intelligent assumptions the <tt class="docutils literal"><span class="pre">Panel</span></tt> class makes is that it can
find a <tt class="docutils literal"><span class="pre">urls.py</span></tt> file in your panel directory which will define a view named
<tt class="docutils literal"><span class="pre">index</span></tt> that handles the default view for that panel. This is what your
<tt class="docutils literal"><span class="pre">urls.py</span></tt> file might look like:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">django.conf.urls.defaults</span> <span class="kn">import</span> <span class="n">patterns</span><span class="p">,</span> <span class="n">url</span>
<span class="kn">from</span> <span class="nn">.views</span> <span class="kn">import</span> <span class="n">IndexView</span>

<span class="n">urlpatterns</span> <span class="o">=</span> <span class="n">patterns</span><span class="p">(</span><span class="s">&#39;&#39;</span><span class="p">,</span>
    <span class="n">url</span><span class="p">(</span><span class="s">r&#39;^$&#39;</span><span class="p">,</span> <span class="n">IndexView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s">&#39;index&#39;</span><span class="p">)</span>
<span class="p">)</span>
</pre></div>
</div>
<p>There&#8217;s nothing there that isn&#8217;t 100% standard Django code. This example
(and Horizon in general) uses the class-based views introduced in Django 1.3
to make code more reusable. Hence the view class is imported in the example
above, and the <tt class="docutils literal"><span class="pre">as_view()</span></tt> method is called in the URL pattern.</p>
<p>This, of course, presumes you have a view class, and takes us into the meat
of writing a <tt class="docutils literal"><span class="pre">Panel</span></tt>.</p>
</div>
<div class="section" id="tables-tabs-and-views">
<h3>Tables, Tabs, and Views<a class="headerlink" href="#tables-tabs-and-views" title="Permalink to this headline">¶</a></h3>
<p>Now we get to the really exciting parts; everything before this was structural.</p>
<p>Starting with the high-level view, our end goal is to create a view (our
<tt class="docutils literal"><span class="pre">IndexView</span></tt> class referenced above) which uses Horizon&#8217;s <tt class="docutils literal"><span class="pre">DataTable</span></tt>
class to display data and Horizon&#8217;s <tt class="docutils literal"><span class="pre">TabGroup</span></tt> class to give us a
user-friendly tabbed interface in the browser.</p>
<p>We&#8217;ll start with the table, combine that with the tabs, and then build our
view from the pieces.</p>
<div class="section" id="defining-a-table">
<h4>Defining a table<a class="headerlink" href="#defining-a-table" title="Permalink to this headline">¶</a></h4>
<p>Horizon provides a <a class="reference internal" href="../ref/tables.html#horizon.tables.DataTable" title="horizon.tables.DataTable"><tt class="xref py py-class docutils literal"><span class="pre">DataTable</span></tt></a> class which simplifies
the vast majority of displaying data to an end-user. We&#8217;re just going to skim
the surface here, but it has a tremendous number of capabilities.</p>
<p>In this case, we&#8217;re going to be presenting data about tables, so let&#8217;s start
defining our table (and a <tt class="docutils literal"><span class="pre">tables.py</span></tt> module:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">horizon</span> <span class="kn">import</span> <span class="n">tables</span>

<span class="k">class</span> <span class="nc">FlockingInstancesTable</span><span class="p">(</span><span class="n">tables</span><span class="o">.</span><span class="n">DataTable</span><span class="p">):</span>
    <span class="n">host</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s">&quot;OS-EXT-SRV-ATTR:host&quot;</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&quot;Host&quot;</span><span class="p">))</span>
    <span class="n">tenant</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s">&#39;tenant_name&#39;</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&quot;Tenant&quot;</span><span class="p">))</span>
    <span class="n">user</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s">&#39;user_name&#39;</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&quot;user&quot;</span><span class="p">))</span>
    <span class="n">vcpus</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s">&#39;flavor_vcpus&#39;</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&quot;VCPUs&quot;</span><span class="p">))</span>
    <span class="n">memory</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s">&#39;flavor_memory&#39;</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&quot;Memory&quot;</span><span class="p">))</span>
    <span class="n">age</span> <span class="o">=</span> <span class="n">tables</span><span class="o">.</span><span class="n">Column</span><span class="p">(</span><span class="s">&#39;age&#39;</span><span class="p">,</span> <span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">&quot;Age&quot;</span><span class="p">))</span>

    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
        <span class="n">name</span> <span class="o">=</span> <span class="s">&quot;instances&quot;</span>
        <span class="n">verbose_name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Instances&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>There are several things going on here... we created a table subclass,
and defined six columns on it. Each of those columns defines what attribute
it accesses on the instance object as the first argument, and since we like to
make everything translatable, we give each column a <tt class="docutils literal"><span class="pre">verbose_name</span></tt> that&#8217;s
marked for translation.</p>
<p>Lastly, we added a <tt class="docutils literal"><span class="pre">Meta</span></tt> class which defines some properties about our
table, notably it&#8217;s (translatable) verbose name, and a semi-unique &#8220;slug&#8221;-like
name to identify it.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">This is a slight simplification from the reality of how the instance
object is actually structured. In reality, accessing the flavor, tenant,
and user attributes on it requires an additional step. This code can be
seen in the example code available on github.</p>
</div>
</div>
<div class="section" id="defining-tabs">
<h4>Defining tabs<a class="headerlink" href="#defining-tabs" title="Permalink to this headline">¶</a></h4>
<p>So we have a table, ready to receive our data. We could go straight to a view
from here, but we can think bigger. In this case we&#8217;re also going to use
Horizon&#8217;s <a class="reference internal" href="../ref/tabs.html#horizon.tabs.TabGroup" title="horizon.tabs.TabGroup"><tt class="xref py py-class docutils literal"><span class="pre">TabGroup</span></tt></a> class. This gives us a clean,
no-fuss tabbed interface to display both our visualization and, optionally,
our data table.</p>
<p>First off, let&#8217;s make a tab for our visualization:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">VizTab</span><span class="p">(</span><span class="n">tabs</span><span class="o">.</span><span class="n">Tab</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Visualization&quot;</span><span class="p">)</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&quot;viz&quot;</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s">&quot;visualizations/flocking/_flocking.html&quot;</span>

    <span class="k">def</span> <span class="nf">get_context_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">None</span>
</pre></div>
</div>
<p>This is about as simple as you can get. Since our visualization will
ultiimately use AJAX to load it&#8217;s data we don&#8217;t need to pass any context
to the template, and all we need to define is the name and which template
it should use.</p>
<p>Now, we also need a tab for our data table:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">.tables</span> <span class="kn">import</span> <span class="n">FlockingInstancesTable</span>

<span class="k">class</span> <span class="nc">DataTab</span><span class="p">(</span><span class="n">tabs</span><span class="o">.</span><span class="n">TableTab</span><span class="p">):</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&quot;Data&quot;</span><span class="p">)</span>
    <span class="n">slug</span> <span class="o">=</span> <span class="s">&quot;data&quot;</span>
    <span class="n">table_classes</span> <span class="o">=</span> <span class="p">(</span><span class="n">FlockingInstancesTable</span><span class="p">,)</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s">&quot;horizon/common/_detail_table.html&quot;</span>
    <span class="n">preload</span> <span class="o">=</span> <span class="bp">False</span>

    <span class="k">def</span> <span class="nf">get_instances_data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">instances</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">get_instances_data</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tab_group</span><span class="o">.</span><span class="n">request</span><span class="p">)</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="n">instances</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="n">exceptions</span><span class="o">.</span><span class="n">handle</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">tab_group</span><span class="o">.</span><span class="n">request</span><span class="p">,</span>
                              <span class="n">_</span><span class="p">(</span><span class="s">&#39;Unable to retrieve instance list.&#39;</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">instances</span>
</pre></div>
</div>
<p>This tab gets a little more complicated. Foremost, it&#8217;s a special type of
tab&#8211;one that handles data tables (and all their associated features)&#8211;and
it also uses the <tt class="docutils literal"><span class="pre">preload</span></tt> attribute to specify that this tab shouldn&#8217;t
be loaded by default. It will instead be loaded via AJAX when someone clicks
on it, saving us on API calls in the vast majority of cases.</p>
<p>Lastly, this code introduces the concept of error handling in Horizon.
The <a class="reference internal" href="../ref/exceptions.html#horizon.exceptions.handle" title="horizon.exceptions.handle"><tt class="xref py py-func docutils literal"><span class="pre">horizon.exceptions.handle()</span></tt></a> function is a centralized error
handling mechanism that takes all the guess-work and inconsistency out of
dealing with exceptions from the API. Use it everywhere.</p>
</div>
<div class="section" id="tying-it-together-in-a-view">
<h4>Tying it together in a view<a class="headerlink" href="#tying-it-together-in-a-view" title="Permalink to this headline">¶</a></h4>
<p>There are lots of pre-built class-based views in Horizon. We try to provide
starting points for all the common combinations of components.</p>
<p>In this case we want a starting view type that works with both tabs and
tables... that&#8217;d be the <a class="reference internal" href="../ref/tabs.html#horizon.tabs.TabbedTableView" title="horizon.tabs.TabbedTableView"><tt class="xref py py-class docutils literal"><span class="pre">TabbedTableView</span></tt></a> class. It takes
the best of the dynamic delayed-loading capabilities tab groups provide and
mixes in the actions and AJAX-updating that tables are capable of with almost
no work on the user&#8217;s end. Let&#8217;s see what the code would look like:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">.tables</span> <span class="kn">import</span> <span class="n">FlockingInstancesTable</span>
<span class="kn">from</span> <span class="nn">.tabs</span> <span class="kn">import</span> <span class="n">FlockingTabs</span>

<span class="k">class</span> <span class="nc">IndexView</span><span class="p">(</span><span class="n">tabs</span><span class="o">.</span><span class="n">TabbedTableView</span><span class="p">):</span>
    <span class="n">tab_group_class</span> <span class="o">=</span> <span class="n">FlockingTabs</span>
    <span class="n">table_class</span> <span class="o">=</span> <span class="n">FlockingInstancesTable</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s">&#39;visualizations/flocking/index.html&#39;</span>
</pre></div>
</div>
<p>That would get us 100% of the way to what we need if this particular
demo didn&#8217;t involve an extra AJAX call to fetch back our visualization
data via AJAX. Because of that we need to override the class&#8217; <tt class="docutils literal"><span class="pre">get()</span></tt>
method to return the right data for an AJAX call:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">.tables</span> <span class="kn">import</span> <span class="n">FlockingInstancesTable</span>
<span class="kn">from</span> <span class="nn">.tabs</span> <span class="kn">import</span> <span class="n">FlockingTabs</span>

<span class="k">class</span> <span class="nc">IndexView</span><span class="p">(</span><span class="n">tabs</span><span class="o">.</span><span class="n">TabbedTableView</span><span class="p">):</span>
    <span class="n">tab_group_class</span> <span class="o">=</span> <span class="n">FlockingTabs</span>
    <span class="n">table_class</span> <span class="o">=</span> <span class="n">FlockingInstancesTable</span>
    <span class="n">template_name</span> <span class="o">=</span> <span class="s">&#39;visualizations/flocking/index.html&#39;</span>

    <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">is_ajax</span><span class="p">()</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;json&quot;</span><span class="p">,</span> <span class="bp">False</span><span class="p">):</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">instances</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">get_instances_data</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">request</span><span class="p">)</span>
            <span class="k">except</span><span class="p">:</span>
                <span class="n">instances</span> <span class="o">=</span> <span class="p">[]</span>
                <span class="n">exceptions</span><span class="o">.</span><span class="n">handle</span><span class="p">(</span><span class="n">request</span><span class="p">,</span>
                                  <span class="n">_</span><span class="p">(</span><span class="s">&#39;Unable to retrieve instance list.&#39;</span><span class="p">))</span>
            <span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">([</span><span class="n">i</span><span class="o">.</span><span class="n">_apiresource</span><span class="o">.</span><span class="n">_info</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">instances</span><span class="p">])</span>
            <span class="k">return</span> <span class="n">http</span><span class="o">.</span><span class="n">HttpResponse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="nb">super</span><span class="p">(</span><span class="n">IndexView</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<p>In this instance, we override the <tt class="docutils literal"><span class="pre">get()</span></tt> method such that if it&#8217;s an
AJAX request and has the GET parameter we&#8217;re looking for, it returns our
instance data in JSON format; otherwise it simply returns the view function
as per the usual.</p>
</div>
<div class="section" id="the-template">
<h4>The template<a class="headerlink" href="#the-template" title="Permalink to this headline">¶</a></h4>
<p>We need three templates here: one for the view, and one for each of our two
tabs. The view template (in this case) can inherit from one of the other
dashboards:</p>
<div class="highlight-python"><pre>{% extends 'syspanel/base.html' %}
{% load i18n %}
{% block title %}{% trans "Flocking" %}{% endblock %}

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

{% block syspanel_main %}
&lt;div class="row-fluid"&gt;
  &lt;div class="span12"&gt;
  {{ tab_group.render }}
  &lt;/div&gt;
&lt;/div&gt;
{% endblock %}</pre>
</div>
<p>This gives us a custom page title, a header, and render our tab group provided
by the view.</p>
<p>For the tabs, the one using the table is handled by a reusable template,
<tt class="docutils literal"><span class="pre">&quot;horizon/common/_detail_table.html&quot;</span></tt>. This is appropriate for any tab that
only displays a single table.</p>
<p>The second tab is a bit of secret sauce for the visualization, but it&#8217;s still
quite simple and can be investigated in the github example.</p>
<p>The takeaway here is that each tab needs a template associated with it.</p>
<p>With all our code in place, the only thing left to do is to integrated it into
our OpenStack Dashboard site.</p>
</div>
</div>
</div>
<div class="section" id="setting-up-a-project">
<h2>Setting up a project<a class="headerlink" href="#setting-up-a-project" title="Permalink to this headline">¶</a></h2>
<p>The vast majority of people will just customize the OpenStack Dashboard
example project that ships with Horizon. As such, this tutorial will
start from that and just illustrate the bits that can be customized.</p>
<div class="section" id="id3">
<h3>Structure<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
<p>A site built on Horizon takes the form of a very typical Django project:</p>
<div class="highlight-python"><pre>site/
  |--__init__.py
  |--manage.py
  |--demo_dashboard/
     |--__init__.py
     |--models.py  # required for Django even if unused
     |--settings.py
     |--templates/
     |--static/</pre>
</div>
<p>The key bits here are that <tt class="docutils literal"><span class="pre">demo_dashboard</span></tt> is on our python path, and that
the <cite>settings.py`</cite> file here will contain our customized Horizon config.</p>
</div>
<div class="section" id="the-settings-file">
<h3>The settings file<a class="headerlink" href="#the-settings-file" title="Permalink to this headline">¶</a></h3>
<p>There are several key things you will generally want to customiz in your
site&#8217;s settings file: specifying custom dashboards and panels, catching your
client&#8217;s exception classes, and (possibly) specifying a file for advanced
overrides.</p>
<div class="section" id="specifying-dashboards">
<h4>Specifying dashboards<a class="headerlink" href="#specifying-dashboards" title="Permalink to this headline">¶</a></h4>
<p>The most basic thing to do is to add your own custom dashboard using the
<tt class="docutils literal"><span class="pre">HORIZON_CONFIG</span></tt> dictionary in the settings file:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">HORIZON_CONFIG</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">&#39;dashboards&#39;</span><span class="p">:</span> <span class="p">(</span><span class="s">&#39;nova&#39;</span><span class="p">,</span> <span class="s">&#39;syspanel&#39;</span><span class="p">,</span> <span class="s">&#39;visualizations&#39;</span><span class="p">,</span> <span class="s">&#39;settings&#39;</span><span class="p">,),</span>
<span class="p">}</span>
</pre></div>
</div>
<p>In this case, we&#8217;ve taken the default Horizon <tt class="docutils literal"><span class="pre">'dashboards'</span></tt> config and
added our <tt class="docutils literal"><span class="pre">visualizations</span></tt> dashboard to it. Note that the name here is the
name of the dashboard&#8217;s module on the python path. It will find our
<tt class="docutils literal"><span class="pre">dashboard.py</span></tt> file inside of it and load both the dashboard and its panels
automatically from there.</p>
</div>
<div class="section" id="error-handling">
<h4>Error handling<a class="headerlink" href="#error-handling" title="Permalink to this headline">¶</a></h4>
<p>Adding custom error handler for your API client is quite easy. While it&#8217;s not
necessary for this example, it would be done by customizing the
<tt class="docutils literal"><span class="pre">'exceptions'</span></tt> value in the <tt class="docutils literal"><span class="pre">HORIZON_CONFIG</span></tt> dictionary:</p>
<div class="highlight-python"><pre>import my_api.exceptions as my_api

'exceptions': {'recoverable': [my_api.Error,
                               my_api.ClientConnectionError],
               'not_found': [my_api.NotFound],
               'unauthorized': [my_api.NotAuthorized]},</pre>
</div>
</div>
<div class="section" id="override-file">
<span id="overrides"></span><h4>Override file<a class="headerlink" href="#override-file" title="Permalink to this headline">¶</a></h4>
<p>The override file is the &#8220;god-mode&#8221; dashboard editor. The hook for this file
sits right between the automatic discovery mechanisms and the final setup
routines for the entire site. By specifying an override file you can alter
any behavior you like in existing code. This tutorial won&#8217;t go in-depth,
but let&#8217;s just say that with great power comes great responsibility.</p>
<p>To specify am override file, you set the <tt class="docutils literal"><span class="pre">'customization_module'</span></tt> value in
the <tt class="docutils literal"><span class="pre">HORIZON_CONFIG</span></tt> dictionary to the dotted python path of your
override module:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">HORIZON_CONFIG</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">&#39;customization_module&#39;</span><span class="p">:</span> <span class="s">&#39;demo_dashboard.overrides&#39;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>This file is capable of adding dashboards, adding panels to existing
dashboards, renaming existing dashboards and panels (or altering other
attributes on them), removing panels from existing dashboards, and so on.</p>
<p>We could say more, but it only gets more dangerous...</p>
</div>
</div>
</div>
<div class="section" id="conclusion">
<h2>Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline">¶</a></h2>
<p>Sadly, the cake was a lie. The information in this &#8220;tutorial&#8221; was never
meant to leave you with a working dashboard. It&#8217;s close. But there&#8217;s
waaaaaay too much javascript involved in the visualization to cover it all
here, and it&#8217;d be irrelevant to Horizon anyway.</p>
<p>If you want to see the finished product, check out the github example
referenced at the beginning of this tutorial.</p>
<p>Clone the repository and simply run <tt class="docutils literal"><span class="pre">./run_tests.sh</span> <span class="pre">--runserver</span></tt>. That&#8217;ll
give you a 100% working dashboard that uses every technique in this tutorial.</p>
<p>What you&#8217;ve learned here, however, is the fundamentals of almost everything
you need to know to start writing interfaces for your own project based on the
components Horizon provides.</p>
<p>If you have questions, or feedback on how this tutorial could be improved,
please feel free to pass them along!</p>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper">
            <h3><a href="../index.html">Table Of Contents</a></h3>
            <ul>
<li><a class="reference internal" href="#">Building on Horizon</a><ul>
<li><a class="reference internal" href="#creating-a-dashboard">Creating a dashboard</a><ul>
<li><a class="reference internal" href="#the-quick-version">The quick version</a></li>
<li><a class="reference internal" href="#structure">Structure</a></li>
<li><a class="reference internal" href="#defining-a-dashboard">Defining a dashboard</a></li>
</ul>
</li>
<li><a class="reference internal" href="#creating-a-panel">Creating a panel</a><ul>
<li><a class="reference internal" href="#id1">The quick version</a></li>
<li><a class="reference internal" href="#id2">Structure</a></li>
<li><a class="reference internal" href="#defining-a-panel">Defining a panel</a></li>
<li><a class="reference internal" href="#urls">URLs</a></li>
<li><a class="reference internal" href="#tables-tabs-and-views">Tables, Tabs, and Views</a><ul>
<li><a class="reference internal" href="#defining-a-table">Defining a table</a></li>
<li><a class="reference internal" href="#defining-tabs">Defining tabs</a></li>
<li><a class="reference internal" href="#tying-it-together-in-a-view">Tying it together in a view</a></li>
<li><a class="reference internal" href="#the-template">The template</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#setting-up-a-project">Setting up a project</a><ul>
<li><a class="reference internal" href="#id3">Structure</a></li>
<li><a class="reference internal" href="#the-settings-file">The settings file</a><ul>
<li><a class="reference internal" href="#specifying-dashboards">Specifying dashboards</a></li>
<li><a class="reference internal" href="#error-handling">Error handling</a></li>
<li><a class="reference internal" href="#override-file">Override file</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#conclusion">Conclusion</a></li>
</ul>
</li>
</ul>

            <h4>Previous topic</h4>
            <p class="topless"><a href="../quickstart.html"
                                  title="previous chapter">Horizon Quickstart</a></p>
            <h4>Next topic</h4>
            <p class="topless"><a href="deployment.html"
                                  title="next chapter">Deploying Horizon</a></p>
            <h3>This Page</h3>
            <ul class="this-page-menu">
              <li><a href="../_sources/topics/tutorial.txt"
                     rel="nofollow">Show Source</a></li>
            </ul>
          <div id="searchbox" style="display: none">
            <h3>Quick search</h3>
              <form class="search" action="../search.html" method="get">
                <input type="text" name="q" size="18" />
                <input type="submit" value="Go" />
                <input type="hidden" name="check_keywords" value="yes" />
                <input type="hidden" name="area" value="default" />
              </form>
              <p class="searchtip" style="font-size: 90%">
              Enter search terms or a module, class or function name.
              </p>
          </div>
          <script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="../genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="../py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="deployment.html" title="Deploying Horizon"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="../quickstart.html" title="Horizon Quickstart"
             accesskey="P">previous</a> |</li>
        <li><a href="../index.html">Horizon 2012.2.3 documentation</a> &raquo;</li> 
      </ul>
    </div>
    <div class="footer">
        &copy; Copyright 2012, OpenStack, LLC.
      Last updated on Feb 08, 2013.
      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3.
    </div>
  </body>
</html>