<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.yoctoproject.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Elliot+Smith</id>
	<title>Yocto Project - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.yoctoproject.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Elliot+Smith"/>
	<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/Special:Contributions/Elliot_Smith"/>
	<updated>2026-04-04T04:30:35Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.5</generator>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Setting_up_a_production_instance_of_Toaster&amp;diff=18169</id>
		<title>Setting up a production instance of Toaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Setting_up_a_production_instance_of_Toaster&amp;diff=18169"/>
		<updated>2016-04-18T10:13:45Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This page is the development version of the documentation to provide the latest information, if you&#039;re using a release please refer to [https://www.yoctoproject.org/documentation/archived the published manual]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A production instance of Toaster is one in which you wish to share the Toaster instance with remote and multiple users. It is also the setup which can cope with heavier loads on the web service. These instructions setup toaster in Build mode where builds and projects are run, viewed and defined by the Toaster web interface.&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* [http://www.yoctoproject.org/docs/2.0/yocto-project-qs/yocto-project-qs.html#packages Build requirements]&lt;br /&gt;
* Apache webserver&lt;br /&gt;
* mod-wsgi for Apache webserver&lt;br /&gt;
* Mysql database server&lt;br /&gt;
&lt;br /&gt;
Ubuntu 14.04.3:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ sudo apt-get install apache2 libapache2-mod-wsgi mysql-server python-virtualenv libmysqlclient-dev python-dev python-mysqldb&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fedora 22/RH:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ sudo dnf install httpd mod_wsgi python-virtualenv gcc mysql-devel&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.&#039;&#039;&#039; Checkout a copy of Poky into the web server directory. We&#039;re going to be using /var/www/toaster.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
  $ mkdir -p /var/www/toaster&lt;br /&gt;
  $ cd /var/www/toaster/&lt;br /&gt;
  $ git clone git://git.yoctoproject.org/poky&lt;br /&gt;
  $ cd poky&lt;br /&gt;
  $ git checkout jethro # change for any release name required&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2.&#039;&#039;&#039; Initialise a virtualenv and install Toaster dependencies. (Use virtualenv to keep the python packages isolated from your system provided packages - not required but recommended, alternative use your OS&#039;s package manager to install the packages)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ cd /var/www/toaster/&lt;br /&gt;
   $ virtualenv venv&lt;br /&gt;
   $ source ./venv/bin/activate&lt;br /&gt;
   $ pip install -r ./poky/bitbake/toaster-requirements.txt&lt;br /&gt;
   $ pip install mysqlclient&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3.&#039;&#039;&#039; Configure toaster edit /var/www/toaster/poky/bitbake/lib/toaster/toastermain/settings.py&lt;br /&gt;
&lt;br /&gt;
Edit the DATABASE settings:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 DATABASES = {&lt;br /&gt;
     &#039;default&#039;: {&lt;br /&gt;
         &#039;ENGINE&#039;: &#039;django.db.backends.mysql&#039;, &lt;br /&gt;
         &#039;NAME&#039;: &#039;toaster_data&#039;,                     &lt;br /&gt;
         &#039;USER&#039;: &#039;toaster&#039;,&lt;br /&gt;
         &#039;PASSWORD&#039;: &#039;yourpasswordhere&#039;,&lt;br /&gt;
         &#039;HOST&#039;: &#039;localhost&#039;,                 &lt;br /&gt;
         &#039;PORT&#039;: &#039;3306&#039;,                      &lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit the [https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/#secret-key SECRET_KEY]:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 SECRET_KEY = &#039;YOUR SECRET RANDOM KEY HERE&#039;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit the STATIC_ROOT:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 STATIC_ROOT = &#039;/var/www/toaster/static_files/&#039;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit BUILD_MODE:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 BUILD_MODE = True&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.&#039;&#039;&#039;&#039; Now add the database and user to your mysql server that we just defined&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ mysql -u root -p&lt;br /&gt;
 mysql&amp;gt; CREATE DATABASE toaster_data;&lt;br /&gt;
 mysql&amp;gt; CREATE USER &#039;toaster&#039;@&#039;localhost&#039; identified by &#039;yourpasswordhere&#039;;&lt;br /&gt;
 mysql&amp;gt; GRANT all on toaster_data.* to &#039;toaster&#039;@&#039;localhost&#039;;&lt;br /&gt;
 mysql&amp;gt; quit&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
n.b. You may want to decide on fewer [https://dev.mysql.com/doc/refman/5.1/en/grant.html privileges] to the toaster user. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;5.&#039;&#039;&#039; Get toaster to create the database schema, default data, update the TOASTER_DIR which is the build work dir and collect up the statically served files&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ cd  /var/www/toaster/poky/&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py syncdb&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py migrate&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py loadconf ./meta-yocto/conf/toasterconf.json&lt;br /&gt;
 $ TOASTER_DIR=/var/www/toaster/poky/ ./bitbake/lib/toaster/manage.py checksettings&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py lsupdates&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py collectstatic&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;6.&#039;&#039;&#039; Add a config file for Toaster to your Apache web server&#039;s configurations available directory.&lt;br /&gt;
&lt;br /&gt;
Ubuntu/Debian put it here: /etc/apache2/conf-available/toaster.conf&lt;br /&gt;
Fedora/RH usually here: /etc/httpd/conf.d/toaster.conf&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Alias /static /var/www/toaster/static_files&lt;br /&gt;
 &amp;lt;Directory /var/www/toaster/static_files&amp;gt;&lt;br /&gt;
 	Order allow,deny&lt;br /&gt;
 	Allow from all&lt;br /&gt;
 	Require all granted&lt;br /&gt;
 &amp;lt;/Directory&amp;gt;&lt;br /&gt;
 WSGIDaemonProcess toaster_wsgi python-path=/var/www/toaster/poky/bitbake/lib/toaster:/var/www/toaster/venv/lib/python2.7/site-packages&lt;br /&gt;
 WSGIScriptAlias / &amp;quot;/var/www/toaster/poky/bitbake/lib/toaster/toastermain/wsgi.py&amp;quot;&lt;br /&gt;
 &amp;lt;Location /&amp;gt;&lt;br /&gt;
     WSGIProcessGroup toaster_wsgi&lt;br /&gt;
 &amp;lt;/Location&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Ubuntu/Debain you will need to enable the config and module in Apache webserver&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ sudo a2enmod wsgi&lt;br /&gt;
   $ sudo a2enconf toaster&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache web server to make sure all new configuration is loaded&lt;br /&gt;
&lt;br /&gt;
Ubuntu/Debian:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ sudo service apache2 restart&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fedora/RH:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ sudo service httpd restart&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;7.&#039;&#039;&#039; Install the build runner service&lt;br /&gt;
&lt;br /&gt;
This service needs to be running in order to dispatch builds the command that needs to be run is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 /var/www/toaster/poky/bitbake/lib/toaster/manage.py runbuilds&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sample script:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 #!/bin/sh&lt;br /&gt;
 # toaster run builds dispatcher&lt;br /&gt;
 cd /var/www/toaster/&lt;br /&gt;
 source ./venv/bin/activate&lt;br /&gt;
 while true; do ./poky/bitbake/lib/toaster/manage.py runbuilds; sleep 3; done&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
N.b. You may wish to add a service entry to your OS&#039;s init system so that it starts up on start up as well as adding a dedicated user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now open up a browser and you can start using Toaster!&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18015</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18015"/>
		<updated>2016-04-05T15:29:52Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [[#Column options|Column options]] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [[#Filter API|Filter API]] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [[#How filters are applied|How filters are applied]] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The [https://docs.djangoproject.com/en/1.9/topics/db/queries/#complex-lookups-with-q Q object] is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** &amp;lt;code&amp;gt;filter=&amp;lt;filter name&amp;gt;:&amp;lt;filter action&amp;gt;&amp;lt;/code&amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, &#039;&#039;filter=outcome_filter:successful_builds&#039;&#039; will map to the &#039;&#039;outcome_filter&#039;&#039; TableFilter and its &#039;&#039;successful_builds&#039;&#039; TableFilterAction.&lt;br /&gt;
** &amp;lt;code&amp;gt;filter_value=&amp;lt;filter value string&amp;gt;&amp;lt;/code&amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: The recordset is filtered by the criteria specified when the action is created (see [[#Add table filter actions|Add table filter actions]] for an example).&lt;br /&gt;
** TableFilterActionDay: A date range clause is constructed at the time the filter action is applied. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot;, the day set for the TableFilterActionDay is &amp;quot;today&amp;quot;, and today is 2016-03-05, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-04 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
** TableFilterActionDateRange: This is similar to TableFilterActionDay, but the user specifies the start and end dates; these are in the filter_value variable in the querystring. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot; and the date range is &amp;quot;2016-03-01,2016-03-04&amp;quot;, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-01 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18014</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18014"/>
		<updated>2016-04-05T15:28:48Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [[#Column options]] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [[#Filter API]] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [[#How filters are applied]] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The [https://docs.djangoproject.com/en/1.9/topics/db/queries/#complex-lookups-with-q Q object] is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** &amp;lt;code&amp;gt;filter=&amp;lt;filter name&amp;gt;:&amp;lt;filter action&amp;gt;&amp;lt;/code&amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, &#039;&#039;filter=outcome_filter:successful_builds&#039;&#039; will map to the &#039;&#039;outcome_filter&#039;&#039; TableFilter and its &#039;&#039;successful_builds&#039;&#039; TableFilterAction.&lt;br /&gt;
** &amp;lt;code&amp;gt;filter_value=&amp;lt;filter value string&amp;gt;&amp;lt;/code&amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: The recordset is filtered by the criteria specified when the action is created (see [[#Add table filter actions]] for an example).&lt;br /&gt;
** TableFilterActionDay: A date range clause is constructed at the time the filter action is applied. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot;, the day set for the TableFilterActionDay is &amp;quot;today&amp;quot;, and today is 2016-03-05, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-04 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
** TableFilterActionDateRange: This is similar to TableFilterActionDay, but the user specifies the start and end dates; these are in the filter_value variable in the querystring. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot; and the date range is &amp;quot;2016-03-01,2016-03-04&amp;quot;, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-01 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18013</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18013"/>
		<updated>2016-04-05T15:27:38Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [[#Column options]] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [[#Filter API]] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [[#How filters are applied]] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The Q object is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** &amp;lt;code&amp;gt;filter=&amp;lt;filter name&amp;gt;:&amp;lt;filter action&amp;gt;&amp;lt;/code&amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, &#039;&#039;filter=outcome_filter:successful_builds&#039;&#039; will map to the &#039;&#039;outcome_filter&#039;&#039; TableFilter and its &#039;&#039;successful_builds&#039;&#039; TableFilterAction.&lt;br /&gt;
** &amp;lt;code&amp;gt;filter_value=&amp;lt;filter value string&amp;gt;&amp;lt;/code&amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: The recordset is filtered by the criteria specified when the action is created (see [[#Add table filter actions]] for an example).&lt;br /&gt;
** TableFilterActionDay: A date range clause is constructed at the time the filter action is applied. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot;, the day set for the TableFilterActionDay is &amp;quot;today&amp;quot;, and today is 2016-03-05, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-04 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
** TableFilterActionDateRange: This is similar to TableFilterActionDay, but the user specifies the start and end dates; these are in the filter_value variable in the querystring. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot; and the date range is &amp;quot;2016-03-01,2016-03-04&amp;quot;, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-01 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18012</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18012"/>
		<updated>2016-04-05T15:26:38Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [#Column options] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [#Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [#How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The Q object is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** &amp;lt;code&amp;gt;filter=&amp;lt;filter name&amp;gt;:&amp;lt;filter action&amp;gt;&amp;lt;/code&amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, &#039;&#039;filter=outcome_filter:successful_builds&#039;&#039; will map to the &#039;&#039;outcome_filter&#039;&#039; TableFilter and its &#039;&#039;successful_builds&#039;&#039; TableFilterAction.&lt;br /&gt;
** &amp;lt;code&amp;gt;filter_value=&amp;lt;filter value string&amp;gt;&amp;lt;/code&amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: The recordset is filtered by the criteria specified when the action is created (see [#Add table filter actions] for an example).&lt;br /&gt;
** TableFilterActionDay: A date range clause is constructed at the time the filter action is applied. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot;, the day set for the TableFilterActionDay is &amp;quot;today&amp;quot;, and today is 2016-03-05, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-04 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
** TableFilterActionDateRange: This is similar to TableFilterActionDay, but the user specifies the start and end dates; these are in the filter_value variable in the querystring. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot; and the date range is &amp;quot;2016-03-01,2016-03-04&amp;quot;, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-01 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18011</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18011"/>
		<updated>2016-04-05T15:25:43Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [#Column options] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [#Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [#How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The Q object is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** &amp;lt;code&amp;gt;filter=&amp;lt;filter name&amp;gt;:&amp;lt;filter action&amp;gt;&amp;lt;/code&amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, filter=outcome_filter:successful_builds will map to the outcome_filter TableFilter, and its successful_builds TableFilterAction.&lt;br /&gt;
** &amp;lt;code&amp;gt;filter_value=&amp;lt;filter value string&amp;gt;&amp;lt;/code&amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: The recordset is filtered by the criteria specified when the action is created (see [#Add table filter actions] for an example).&lt;br /&gt;
** TableFilterActionDay: A date range clause is constructed at the time the filter action is applied. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot;, the day set for the TableFilterActionDay is &amp;quot;today&amp;quot;, and today is 2016-03-05, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-04 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
** TableFilterActionDateRange: This is similar to TableFilterActionDay, but the user specifies the start and end dates; these are in the filter_value variable in the querystring. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot; and the date range is &amp;quot;2016-03-01,2016-03-04&amp;quot;, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-01 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18010</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=18010"/>
		<updated>2016-04-05T15:24:51Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [#Column options] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [#Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [#How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The Q object is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** filter=&amp;amp;lt;filter name&amp;amp;gt;:&amp;amp;lt;filter action&amp;amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, filter=outcome_filter:successful_builds will map to the outcome_filter TableFilter, and its successful_builds TableFilterAction.&lt;br /&gt;
** filter_value=&amp;amp;lt;filter value string&amp;amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: The recordset is filtered by the criteria specified when the action is created (see [#Add table filter actions] for an example).&lt;br /&gt;
** TableFilterActionDay: A date range clause is constructed at the time the filter action is applied. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot;, the day set for the TableFilterActionDay is &amp;quot;today&amp;quot;, and today is 2016-03-05, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-04 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
** TableFilterActionDateRange: This is similar to TableFilterActionDay, but the user specifies the start and end dates; these are in the filter_value variable in the querystring. For example, if the field the filter action applies to is &amp;quot;completed_on&amp;quot; and the date range is &amp;quot;2016-03-01,2016-03-04&amp;quot;, the query clause (in pseudo-SQL) is &amp;quot;completed_on &amp;gt;= &#039;2016-03-01 00:00:00&#039; AND completed_on &amp;lt;= &#039;2016-03-04 23:59:59&#039;&amp;quot;.&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17998</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17998"/>
		<updated>2016-04-05T14:26:46Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The Q object is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** filter=&amp;amp;lt;filter name&amp;amp;gt;:&amp;amp;lt;filter action&amp;amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, filter=outcome_filter:successful_builds will map to the outcome_filter TableFilter, and its successful_builds TableFilterAction.&lt;br /&gt;
** filter_value=&amp;amp;lt;filter value string&amp;amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: the recordset is filtered by a ???&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17997</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17997"/>
		<updated>2016-04-05T14:23:36Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier: one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give a bit more detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring (see the next section).&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied. The Q object is part of the Django API; it should reference field names which are present in the queryset to be filtered and can use any criteria available to Q objects. In the case of the MiniBuildsTable, the queryset consists of Build objects; the Q(outcome=Build.SUCCEEDED) object is used to filter this queryset, so we effectively get the result of Build.objects.all().filter(Q(outcome=Build.SUCCEEDED)) when the action is applied.&lt;br /&gt;
&lt;br /&gt;
For examples of how to add TableFilterActionDay and TableFilterActionDateRange filter actions, see the BuildsTable class in tables.py.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, new data for the table is requested, using the filter name, filter action, and filter value from the querystring. These parameters are used to filter the records which are shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* A filter icon for a column has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by an action is part of the data returned by the back-end; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** filter=&amp;amp;lt;filter name&amp;amp;gt;:&amp;amp;lt;filter action&amp;amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, filter=outcome_filter:successful_builds will map to the outcome_filter TableFilter, and its successful_builds TableFilterAction.&lt;br /&gt;
** filter_value=&amp;amp;lt;filter value string&amp;amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter action to the queryset (as well as any existing search string). Each filter action has a set of criteria which it applies to the queryset, as follows:&lt;br /&gt;
** TableFilterActionToggle: the recordset is filtered by&lt;br /&gt;
* The filtered queryset is returned as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17994</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17994"/>
		<updated>2016-04-05T14:06:16Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier; one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how this will render:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilteractions.png|750px|Adding outcome filter actions to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give some detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring.&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, the page is requested with the filter name, filter action, and filter value in the querystring. These parameters are then used to filter the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* Each filter icon has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by that action is part of the data returned by the server; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** filter=&amp;amp;lt;filter name&amp;amp;gt;:&amp;amp;lt;filter action&amp;amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, filter=outcome_filter:successful_builds will map to the outcome_filter TableFilter, and its successful_builds TableFilterAction.&lt;br /&gt;
** filter_value=&amp;amp;lt;filter value string&amp;amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter to the queryset (as well as any existing search string) and returns it as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-outcomefilteractions.png&amp;diff=17993</id>
		<title>File:MiniBuildsTable-outcomefilteractions.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-outcomefilteractions.png&amp;diff=17993"/>
		<updated>2016-04-05T14:06:04Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17992</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17992"/>
		<updated>2016-04-05T14:04:10Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for the column.&lt;br /&gt;
# Assign the filter to the column in the ToasterTable.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Add a table filter ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Internally, add_filter() uses a TableFilterMap in the ToasterTable, which maps from column names to TableFilters.&lt;br /&gt;
&lt;br /&gt;
Note that the filter has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associate a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword passed to add_column().)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
outcome_filter is the string used to refer to the filter in the querystring; see the section [How filters are applied] for more details about how filters are applied.&lt;br /&gt;
&lt;br /&gt;
=== Add table filter actions ===&lt;br /&gt;
&lt;br /&gt;
The next step is to add actions to the filter.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. The following types of filter action (subclasses of TableFilterAction) are already implemented:&lt;br /&gt;
&lt;br /&gt;
* TableFilterActionToggle: filter the records shown in the table by some arbitrary criteria; the action is either on or off.&lt;br /&gt;
* TableFilterActionDay: filter the records shown by day (yesterday or today).&lt;br /&gt;
* TableFilterActionDateRange: filter the records shown by a from/to date range.&lt;br /&gt;
&lt;br /&gt;
In the case of TableFilterActionDay and TableFilterActionDateRange, you specify the field which is used for the filter action. In the case of TableFilterActionToggle, you can use arbitrary criteria for the action.&lt;br /&gt;
&lt;br /&gt;
To add an action to a filter, create an instance of the desired action class and use the TableFilter.add_action() method to associate it with a filter. For example, here&#039;s how to add two actions to the outcome_filter defined earlier; one to show successful builds, and the other to show failed builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
from toastergui.tablefilter import TableFilterActionToggle&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;successful_builds&#039;,&lt;br /&gt;
            &#039;Successful builds&#039;,&lt;br /&gt;
            Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        failed_builds_action = TableFilterActionToggle(&lt;br /&gt;
            &#039;failed_builds&#039;,&lt;br /&gt;
            &#039;Failed builds&#039;,&lt;br /&gt;
            Q(outcome=Build.FAILED)&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        outcome_filter.add_action(successful_builds_action)&lt;br /&gt;
        outcome_filter.add_action(failed_builds_action)&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I&#039;ll break this down further to give some detail about how the filter action is created. Here&#039;s the code which creates the filter action to show successful builds only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
successful_builds_action = TableFilterActionToggle(&lt;br /&gt;
    &#039;successful_builds&#039;,&lt;br /&gt;
    &#039;Successful builds&#039;,&lt;br /&gt;
    Q(outcome=Build.SUCCEEDED)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The arguments to TableFilterActionToggle have the following meanings:&lt;br /&gt;
* &#039;successful_builds&#039; is the name of the action. This is used to map from the filter parameter in the querystring.&lt;br /&gt;
* &#039;Successful builds&#039; is the label shown next to the radio button which activates this action in the popup.&lt;br /&gt;
* Q(outcome=Build.SUCCEEDED) shows the criteria used to filter the records in the table&#039;s queryset when the filter action is applied.&lt;br /&gt;
&lt;br /&gt;
The next section explains how we go from clicking on a radio button in the filter popup to filtering the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
=== How filters are applied ===&lt;br /&gt;
&lt;br /&gt;
The short version:&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is rendered, a filter icon is shown on any column which has a filter_name defined for it. Clicking on this icon populates the filter dialog, then opens it so the user can select a filter to apply. When the user clicks on the &amp;quot;Apply&amp;quot; button in the dialog, the page is requested with the filter name, filter action, and filter value in the querystring. These parameters are then used to filter the records shown in the table.&lt;br /&gt;
&lt;br /&gt;
The long version:&lt;br /&gt;
&lt;br /&gt;
* Each filter icon has a filter name associated with it.&lt;br /&gt;
* When a filter icon is clicked, the Toaster UI makes an Ajax request to the Toaster back-end for data about that filter name.&lt;br /&gt;
* When the filter data is received (in JSON format), the filter dialog is populated with radio buttons (one per action), labels (one per action) and any additional fields (e.g. date range fields for TableFilterActionDateRange actions). The count of records which will be returned by that action is part of the data returned by the server; if this is 0, the label and radio button are disabled. See static/js/table.js for the code which populates the filter dialog.&lt;br /&gt;
* The dialog is opened so the user can choose a filter to apply. The user clicks radio buttons, fills in fields etc.&lt;br /&gt;
* When the user clicks on the &amp;quot;Apply&amp;quot; button, the URL for the page is modified in place to reflect the filter criteria. The filter is represented in the URL by two querystring parameters:&lt;br /&gt;
** filter=&amp;amp;lt;filter name&amp;amp;gt;:&amp;amp;lt;filter action&amp;amp;gt; : the filter name maps to the name used when creating the TableFilter object; and the filter action corresponds to one of the names of a TableFilterAction object added to the TableFilter. For example, filter=outcome_filter:successful_builds will map to the outcome_filter TableFilter, and its successful_builds TableFilterAction.&lt;br /&gt;
** filter_value=&amp;amp;lt;filter value string&amp;amp;gt; : for a TableFilterActionToggle filter, this is always &amp;quot;on&amp;quot;, to show that the filter is applied; for a TableFilterActionDay this is either &amp;quot;today&amp;quot; or &amp;quot;yesterday&amp;quot;; for a TableFilterActionDateRange, this is a &amp;quot;from,to&amp;quot; date range in the format &amp;quot;2015-12-09,2015-12-11&amp;quot;.&lt;br /&gt;
* The table data is fetched via Ajax, using the filter and filter_value parameters as part of the URL. These set the filter name, action and value to use for filtering.&lt;br /&gt;
* The back-end applies the requested filter to the queryset (as well as any existing search string) and returns it as JSON.&lt;br /&gt;
* The Toaster UI code redraws the table with the filtered queryset.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-outcomefilter.png&amp;diff=17991</id>
		<title>File:MiniBuildsTable-outcomefilter.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-outcomefilter.png&amp;diff=17991"/>
		<updated>2016-04-05T13:06:24Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17990</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17990"/>
		<updated>2016-04-05T13:06:03Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/toastergui/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/toastergui/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
Adding other data to the context for use in the page template is the same as for other Django TemplateView objects. See [https://docs.djangoproject.com/en/1.9/topics/class-based-views/generic-display/#adding-extra-context the Django documentation] for full details.&lt;br /&gt;
&lt;br /&gt;
As a simple worked example, here&#039;s how we could show the most recent Toaster build in the Mini Builds page. First, we need to implement get_context_data() and add our own data to it; in this case, a &amp;quot;most_recent_build&amp;quot; property containing a Build object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we now use the most_recent_build property we added in get_context_data(), we modify the pagecontent block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
==== Dynamic data for other templates ====&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the URL (for example, you want an object retrieved using the ID in the URL), the pattern is similar to the above. The main difference is that you make use of the kwargs parameter passed to get_context_data(), which contains parameters derived from the page&#039;s URL.&lt;br /&gt;
&lt;br /&gt;
As an example, the project builds table shows all of the builds for a project in a ToasterTable. It makes sense to add the project to the context so that its name can be shown at the top of the page. For a URL like http://localhost:8000/toastergui/project/X/builds, Toaster assigns the &amp;quot;X&amp;quot; in the URL to a parameter called &#039;&#039;pid&#039;&#039;; this can be retrieved in get_context_data() and used to fetch the project for which we are showing builds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class ProjectBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        context = super(ProjectBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # use the pid parameter extracted from the URL&lt;br /&gt;
        context[&#039;project&#039;] = Project.objects.get(pk=kwargs[&#039;pid&#039;])&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can then be referenced the usual way in the template (e.g. &amp;lt;code&amp;gt;{{project}}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
Search is automatically enabled on a ToasterTable. However, you may find that you are unable to search on the fields you would like to. This section explains how to fix that.&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable searches against the queryset&#039;s model, using an OR query with icontains (case insensitive, contains string) matching. The fields used for the search are defined by the search_allowed_fields property of the model.&lt;br /&gt;
&lt;br /&gt;
For MiniBuildsTable, the queryset&#039;s model is orm.models.Build; search_allowed_fields (at the time of writing) for Build is set to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Doing a search for a string like &amp;quot;test&amp;quot; would therefore construct a query OR clause like the following (pseudo-SQL):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
... WHERE machine LIKE &#039;%test%&#039; OR cooker_log_path LIKE &#039;%test%&#039; OR target.target LIKE &#039;%test%&#039;&lt;br /&gt;
OR target.target_image_file.file_name LIKE &#039;%test%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The actual SQL clause is far more complicated, as Django would have to use various JOIN statements to combine rows from multiple database tables.)&lt;br /&gt;
&lt;br /&gt;
Note that the search_allowed_fields don&#039;t have to be fields of the model: they can be fields in related models (here, the Target and TargetImageFile models related to a Build).&lt;br /&gt;
&lt;br /&gt;
This means that it&#039;s not possible to search by project name in the MiniBuildsTable. To make this possible, we could modify search_allowed_fields for the Build model to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[&#039;machine&#039;, &#039;cooker_log_path&#039;, &#039;target__target&#039;, &#039;target__target_image_file__file_name&#039;, &#039;project__name&#039;]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would now allow the Mini Builds table to be searched by project name as well.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you would add a filter which filters the builds based on the time when the build was completed.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. A filter also automatically gets a default action which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
To add a filter to a column, you do the following:&lt;br /&gt;
&lt;br /&gt;
# Create a TableFilter object for a column.&lt;br /&gt;
# Assign the filter to a column in the ToasterTable. This uses a TableFilterMap in the ToasterTable, which is a map from column names to TableFilters.&lt;br /&gt;
# Attach TableFilterAction objects to the TableFilter.&lt;br /&gt;
&lt;br /&gt;
These steps are explained in detail below.&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
To create a TableFilter, instantiate it with a unique identifier (unique to the ToasterTable) and a text string which is displayed in the popup for the filter. The filters should be defined in the setup_filters() method for the ToasterTable.&lt;br /&gt;
&lt;br /&gt;
Once the filter is defined, add it to the ToasterTable using the add_filter() method.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of a TableFilter for the outcome column of MiniBuildsTable:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# already imported in tables.py, but shown here for completeness&lt;br /&gt;
from toastergui.tablefilter import TableFilter&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def setup_filters(self, *args, **kwargs):&lt;br /&gt;
        # filter by outcome (succeeded or failed)&lt;br /&gt;
        outcome_filter = TableFilter(&lt;br /&gt;
            &#039;outcome_filter&#039;,&lt;br /&gt;
            &#039;Filter builds by outcome&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        # add the filter to the ToasterTable&lt;br /&gt;
        self.add_filter(outcome_filter)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that his has no actions, so won&#039;t actually filter the table yet.&lt;br /&gt;
&lt;br /&gt;
=== Associating a filter with a column ===&lt;br /&gt;
&lt;br /&gt;
To associate the filter with a column, pass the name of the filter in the filter_name argument for the column. For example, to associate the outcome_filter defined above with the outcome column, change setup_columns() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # ... other column definitions ...&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template,&lt;br /&gt;
                        filter_name=&#039;outcome_filter&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note the additional filter_name keyword.)&lt;br /&gt;
&lt;br /&gt;
Now if you visit http://localhost:8000/toastergui/minibuilds, the outcome column in the table should show the filter icon; clicking on this opens the popup which allows a user to select the filter to apply. Because we have no actions on the filter, the only option is the &amp;quot;All&amp;quot; one:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcomefilter.png|750px|Adding an outcome filter to the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-mostrecentbuild.png&amp;diff=17981</id>
		<title>File:MiniBuildsTable-mostrecentbuild.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-mostrecentbuild.png&amp;diff=17981"/>
		<updated>2016-04-05T11:38:01Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17980</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17980"/>
		<updated>2016-04-05T11:37:49Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == 0 %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == 1 %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, static_data_template is used as we don&#039;t just want the value of the outcome field for the row: we also want to create a link. The template is slightly more complex than previous ones, so it&#039;s in a variable to make the code cleaner. Note the use of the built-in Django &#039;&#039;url&#039;&#039; template tag to get the URL for the build dashboard.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later date. It would be better to use the constants for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in the queryset. This data may be shown on the page, or may influence how other data is rendered. For example, in the previous section, the outcome was displayed according to whether the build was a success or had some other outcome; but, rather than using hard-coded integer values, it would be better to use the Build.SUCCEEDED and Build.FAILED constants for this.&lt;br /&gt;
&lt;br /&gt;
As another example, the Mini Builds page could show the time of the most recent build. While this could be done with some work in the template, it would be much cleaner to add a variable for &amp;quot;most_recent_build&amp;quot; to the template context instead.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data must be added to the extra context data for the page. This can be done in one of two ways, depending on how you want to use it:&lt;br /&gt;
&lt;br /&gt;
# If the data needs to be available to column templates, it should be added to the &#039;&#039;static_context_extra&#039;&#039; dictionary for the ToasterTable. For example, because we need to access constants on the Build class in the column templates, we should add Build to static_context_extra.&lt;br /&gt;
# If the data needs to be available to other templates (e.g. minibuilds.html in the case of Mini Builds), it should be set in the &#039;&#039;get_context_data()&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
==== Data for column templates ====&lt;br /&gt;
&lt;br /&gt;
Data can be made available to column templates for a ToasterTable by adding it to the static_context_extra dictionary inside the __init__() method. This makes the data available for use in column templates (set using static_data_template). However, this data is &#039;&#039;only&#039;&#039; accessible from column templates, and is &#039;&#039;not&#039;&#039; available to the other templates used to render the page.&lt;br /&gt;
&lt;br /&gt;
For example, to add the Build class to static_context_extra so that it can be used in a column template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
        # add the Build class to the static context&lt;br /&gt;
        self.static_context_extra[&#039;Build&#039;] = Build&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        # reference constants on Build from a column template&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;a href=&amp;quot;{% url &#039;builddashboard&#039; data.id %}&amp;quot;&amp;gt;&lt;br /&gt;
            {% if data.outcome == extra.Build.SUCCEEDED %}&lt;br /&gt;
                succeeded&lt;br /&gt;
            {% elif data.outcome == extra.Build.FAILED %}&lt;br /&gt;
                failed&lt;br /&gt;
            {% endif %}&lt;br /&gt;
        &amp;lt;/a&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        # ... the rest of setup_columns is the same ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To refer to data from static_context_extra in a column template, use the &#039;&#039;extra&#039;&#039; keyword (similar to how the &#039;&#039;data&#039;&#039; keyword works). In this case, I referenced the constants on the Build class with &#039;&#039;extra.Build.SUCCEEDED&#039;&#039; and &#039;&#039;extra.Build.FAILED&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page renders the same as before, but we no longer have hard-coded integer values in the column templates.&lt;br /&gt;
&lt;br /&gt;
==== Data for other templates ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other methods remain as they were ...&lt;br /&gt;
&lt;br /&gt;
    def get_context_data(self, **kwargs):&lt;br /&gt;
        # invoke the super class&#039; method, to include data like the page&lt;br /&gt;
        # title in the context&lt;br /&gt;
        context = super(MiniBuildsTable, self).get_context_data(**kwargs)&lt;br /&gt;
&lt;br /&gt;
        # add the most recent build to the context&lt;br /&gt;
        all_builds = Build.objects.all().order_by(&#039;-completed_on&#039;)&lt;br /&gt;
        context[&#039;most_recent_build&#039;] = all_builds.first()&lt;br /&gt;
&lt;br /&gt;
        return context&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of the properties on the context object returned by get_context_data() are available in the page template, as per the context for a standard Django template. To show the most recent build in the minibuilds.html template, we can now use the most_recent_build property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- SHOW MOST RECENT BUILD --&amp;gt;&lt;br /&gt;
  {% if most_recent_build %}&lt;br /&gt;
    &amp;lt;p&amp;gt;Most recent build completed: {{most_recent_build.completed_on}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result looks like this:&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-mostrecentbuild.png|750px|Showing the most recent build in the MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
If you need different data in the context depending on the querystring (for example, you want an object retrieved using the ID in the querystring), the pattern is similar to the above.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Updating the page when the queryset changes ===&lt;br /&gt;
&lt;br /&gt;
We could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17977</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17977"/>
		<updated>2016-04-04T18:21:15Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|750px|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        {% if data.outcome == 0 %}&lt;br /&gt;
          succeeded&lt;br /&gt;
        {% elif data.outcome == 1 %}&lt;br /&gt;
          failed&lt;br /&gt;
        {% endif %}&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, I have to use static_data_template, as I want to put more into the cells for this column than just the value of the outcome field for the row. The template is slightly more complex, so I created a variable to hold its content to keep the code layout clean.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-outcome.png|750px|Adding an outcome column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later data. It would be better to use the enumerator for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in each record of the queryset, but which should be shown on the page or may influence how other data is rendered. For example, in the previous section, I needed to change how the outcome was displayed according to whether the build was a success or had some other outcome; but I wanted to use the Build.SUCCEEDED and Build.FAILED constants to do this, rather than hard-coding integer values.&lt;br /&gt;
&lt;br /&gt;
As another example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data can be added to the extra context data for the page. This can be done in one of two ways, depending on the type of data.&lt;br /&gt;
&lt;br /&gt;
# If the data is static and doesn&#039;t change according to any parameters passed to the view, it can be set in the __init__() method for the ToasterTable. For example, the Build class doesn&#039;t change, so that can be added in init.&lt;br /&gt;
# If the data is different each time the page is rendered, it can be set in . This is typically used where the additional context data is dependent on the querystring. For example, in the project builds page, we add different data to the context depending on which project we are showing builds for.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Updating the page when the queryset changes ===&lt;br /&gt;
&lt;br /&gt;
We could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-projectname.png&amp;diff=17976</id>
		<title>File:MiniBuildsTable-projectname.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-projectname.png&amp;diff=17976"/>
		<updated>2016-04-04T18:19:16Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17975</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17975"/>
		<updated>2016-04-04T18:17:00Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-projectname.png|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        {% if data.outcome == 0 %}&lt;br /&gt;
          succeeded&lt;br /&gt;
        {% elif data.outcome == 1 %}&lt;br /&gt;
          failed&lt;br /&gt;
        {% endif %}&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, I have to use static_data_template, as I want to put more into the cells for this column than just the value of the outcome field for the row. The template is slightly more complex, so I created a variable to hold its content to keep the code layout clean.&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later data. It would be better to use the enumerator for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in each record of the queryset, but which should be shown on the page or may influence how other data is rendered. For example, in the previous section, I needed to change how the outcome was displayed according to whether the build was a success or had some other outcome; but I wanted to use the Build.SUCCEEDED and Build.FAILED constants to do this, rather than hard-coding integer values.&lt;br /&gt;
&lt;br /&gt;
As another example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data can be added to the extra context data for the page. This can be done in one of two ways, depending on the type of data.&lt;br /&gt;
&lt;br /&gt;
# If the data is static and doesn&#039;t change according to any parameters passed to the view, it can be set in the __init__() method for the ToasterTable. For example, the Build class doesn&#039;t change, so that can be added in init.&lt;br /&gt;
# If the data is different each time the page is rendered, it can be set in . This is typically used where the additional context data is dependent on the querystring. For example, in the project builds page, we add different data to the context depending on which project we are showing builds for.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Updating the page when the queryset changes ===&lt;br /&gt;
&lt;br /&gt;
We could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-outcome.png&amp;diff=17974</id>
		<title>File:MiniBuildsTable-outcome.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable-outcome.png&amp;diff=17974"/>
		<updated>2016-04-04T18:15:29Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable.png&amp;diff=17973</id>
		<title>File:MiniBuildsTable.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=File:MiniBuildsTable.png&amp;diff=17973"/>
		<updated>2016-04-04T18:15:13Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17972</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17972"/>
		<updated>2016-04-04T18:14:16Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-project__name.png|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        {% if data.outcome == 0 %}&lt;br /&gt;
          succeeded&lt;br /&gt;
        {% elif data.outcome == 1 %}&lt;br /&gt;
          failed&lt;br /&gt;
        {% endif %}&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, I have to use static_data_template, as I want to put more into the cells for this column than just the value of the outcome field for the row. The template is slightly more complex, so I created a variable to hold its content to keep the code layout clean.&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later data. It would be better to use the enumerator for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in each record of the queryset, but which should be shown on the page or may influence how other data is rendered. For example, in the previous section, I needed to change how the outcome was displayed according to whether the build was a success or had some other outcome; but I wanted to use the Build.SUCCEEDED and Build.FAILED constants to do this, rather than hard-coding integer values.&lt;br /&gt;
&lt;br /&gt;
As another example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data can be added to the extra context data for the page. This can be done in one of two ways, depending on the type of data.&lt;br /&gt;
&lt;br /&gt;
# If the data is static and doesn&#039;t change according to any parameters passed to the view, it can be set in the __init__() method for the ToasterTable. For example, the Build class doesn&#039;t change, so that can be added in init.&lt;br /&gt;
# If the data is different each time the page is rendered, it can be set in . This is typically used where the additional context data is dependent on the querystring. For example, in the project builds page, we add different data to the context depending on which project we are showing builds for.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Updating the page when the queryset changes ===&lt;br /&gt;
&lt;br /&gt;
We could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17971</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17971"/>
		<updated>2016-04-04T18:13:01Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render a value from a row into the HTML for the page. The data.completed_on reference in the template demonstrates how to access field values from the record for the row: the object representing the current row can be reference via the &#039;data&#039; object in the static_data_template string. The whole of Django&#039;s template machinery is available when specifying how to render properties of the record for a row: you can include other templates and use filters and template tags (your own or Django&#039;s). In this case, the date filter is used to format the date into a human-readable form.&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [???adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
Finally, you should define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would go in templates/minibuilds.html:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to create a template by adapting an existing one (particularly if you are porting a table to ToasterTable).&lt;br /&gt;
&lt;br /&gt;
Note that the template above extends base.html, which provides the header/footer for Toaster itself but doesn&#039;t have a side menu. For an example of a more complex page which does have a side menu, see generic-toastertable-page.html and its parent template, baseprojectpage.html.&lt;br /&gt;
&lt;br /&gt;
The important points to remember, though, are shown in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table class to render it, passing the name of the template file. (Usually, a view mapping would specify a function in the views.py file.)&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable.png|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
=== Aside: turning off ToasterTable caching ===&lt;br /&gt;
&lt;br /&gt;
By default, ToasterTable caches data to prevent the same data being fetched multiple times. However, during development, this may mean that you see stale data in the table (for example, I changed the setup_columns() method in MiniBuildsTable to remove a column, but when I refreshed the page, the column was still present).&lt;br /&gt;
&lt;br /&gt;
To get around this, pass the nocache option in the querystring in your browser, e.g.&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds?nocache=true&lt;br /&gt;
&lt;br /&gt;
This ensures that ToasterTable doesn&#039;t cache data between page refreshes.&lt;br /&gt;
&lt;br /&gt;
== Further ToasterTable configuration ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more non-table data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Column options ===&lt;br /&gt;
&lt;br /&gt;
As stated previously, columns are added to the table inside the setup_columns() method of your ToasterTable subclass. The following options can be set when adding a column to a table:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;title&#039;&#039;&#039; (string): The heading for the column.&lt;br /&gt;
* &#039;&#039;&#039;help_text&#039;&#039;&#039; (string; optional): Text which describes the column content of the column; this is shown in a popover when the question mark next to the column heading is hovered over. If not set, the column doesn&#039;t have a help icon.&lt;br /&gt;
* &#039;&#039;&#039;hideable&#039;&#039;&#039; (boolean; default=True): True if the user can hide the column (using the &amp;quot;Edit columns&amp;quot; drop-down).&lt;br /&gt;
* &#039;&#039;&#039;hidden&#039;&#039;&#039; (boolean; default=False): True if the column is hidden by default; the user can show the column using the &amp;quot;Edit columns&amp;quot; drop-down.&lt;br /&gt;
* &#039;&#039;&#039;field_name&#039;&#039;&#039; (string; optional): Name of the property to render into the field. Note that this can be a property or method on the model being rendered (e.g. for a Build object, completed_on) ; it could also be a property of a related object (e.g. for a Build, project__name).&lt;br /&gt;
* &#039;&#039;&#039;static_data_name&#039;&#039;&#039; (string; optional): This should &#039;&#039;not&#039;&#039; be set if field_name is set, but &#039;&#039;must&#039;&#039; be set if static_data_template is set.&lt;br /&gt;
* &#039;&#039;&#039;static_data_template&#039;&#039;&#039; (string; optional): The template to render for each row; the data for the row is interpolated into the template. This is more flexible than field_name, as you can add links, conditional statements and additional formatting each time the cell for this column is rendered for a record.&lt;br /&gt;
* &#039;&#039;&#039;orderable&#039;&#039;&#039; (boolean; default=False): True if the table can be ordered by this column. Note that if this is True, either field_name or static_data_name should match a name of one of the fields in the queryset for the ToasterTable. If not, Toaster will not be able to sort the table by that column.&lt;br /&gt;
* &#039;&#039;&#039;filter_name&#039;&#039;&#039; (string; optional): Name of the TableFilter associated with this column, which adds filtering behaviour for this column; see the [???Filter API] section for full details.&lt;br /&gt;
&lt;br /&gt;
As an example, I&#039;ll add two columns to the Mini Builds table.&lt;br /&gt;
&lt;br /&gt;
First, a column to show the project name for the build:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The rest of the MiniBuildsTable remains the same: only setup_columns() changes. In this case, I&#039;m showing the project name for the build using the field_name property for the column. This is simple, but doesn&#039;t allow me to modify how the value is formatted or add any HTML elements around it.&lt;br /&gt;
&lt;br /&gt;
Try http://localhost:8000/minibuilds/ again and you should see the new column. Note that this column can be hidden in the &amp;quot;Edit columns&amp;quot; drop-down, and can also be used to order the table.&lt;br /&gt;
&lt;br /&gt;
[[File:MiniBuildsTable-project__name.png|Adding a project name column to MiniBuildsTable]]&lt;br /&gt;
&lt;br /&gt;
Next, a column to show the outcome for the build, with a link to that build&#039;s dashboard:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    # ... other code ...&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        outcome_template = &#039;&#039;&#039;&lt;br /&gt;
        {% if data.outcome == 0 %}&lt;br /&gt;
          succeeded&lt;br /&gt;
        {% elif data.outcome == 1 %}&lt;br /&gt;
          failed&lt;br /&gt;
        {% endif %}&lt;br /&gt;
        &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Project&#039;,&lt;br /&gt;
                        help_text=&#039;The project associated with this build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        field_name=&#039;project__name&#039;)&lt;br /&gt;
&lt;br /&gt;
        self.add_column(title=&#039;Outcome&#039;,&lt;br /&gt;
                        help_text=&#039;The outcome of the build&#039;,&lt;br /&gt;
                        hideable=True,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;outcome&#039;,&lt;br /&gt;
                        static_data_template=outcome_template)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, I have to use static_data_template, as I want to put more into the cells for this column than just the value of the outcome field for the row. The template is slightly more complex, so I created a variable to hold its content to keep the code layout clean.&lt;br /&gt;
&lt;br /&gt;
There is one issue with outcome_template: it compares the outcome field&#039;s value with integer values (0 for succeeded, 1 for failed) to determine what to show in the cell. This is a bit opaque for any developer coming to the project later, and also fragile if the integer values representing build outcomes change at a later data. It would be better to use the enumerator for Build outcomes (Build.SUCCEEDED, Build.FAILED) instead. However, the Build object is not accessible to the template, as it isn&#039;t passed in with the template context. The next section explains how to deal with this and similar missing context.&lt;br /&gt;
&lt;br /&gt;
=== Additional context data ===&lt;br /&gt;
&lt;br /&gt;
Sometimes a page will require data which is not in each record of the queryset, but which should be shown on the page or may influence how other data is rendered. For example, in the previous section, I needed to change how the outcome was displayed according to whether the build was a success or had some other outcome; but I wanted to use the Build.SUCCEEDED and Build.FAILED constants to do this, rather than hard-coding integer values.&lt;br /&gt;
&lt;br /&gt;
As another example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page.&lt;br /&gt;
&lt;br /&gt;
In both cases, the required data can be added to the extra context data for the page. This can be done in one of two ways, depending on the type of data.&lt;br /&gt;
&lt;br /&gt;
# If the data is static and doesn&#039;t change according to any parameters passed to the view, it can be set in the __init__() method for the ToasterTable. For example, the Build class doesn&#039;t change, so that can be added in init.&lt;br /&gt;
# If the data is different each time the page is rendered, it can be set in . This is typically used where the additional context data is dependent on the querystring. For example, in the project builds page, we add different data to the context depending on which project we are showing builds for.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Updating the page when the queryset changes ===&lt;br /&gt;
&lt;br /&gt;
We could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Search ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17970</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17970"/>
		<updated>2016-04-04T15:08:37Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render the value from the record into the HTML for the page. The data.completed_on reference in the template demonstrates how to access&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Add the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
You will also need to define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would be in templates/minibuilds.html.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to modify an existing template (particularly if you are porting a table to ToasterTable). Note that this template extends the base.html template, which provides the header/footer for Toaster itself, but doesn&#039;t have a side menu. For an example of a more complex page, see builds-toastertable.html.&lt;br /&gt;
&lt;br /&gt;
The important things to remember, though, are in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table to render it, passing the name of the template file.&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
== More ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Adding more columns ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Column options&lt;br /&gt;
&lt;br /&gt;
Adding a column to a table:&lt;br /&gt;
- Sorting by a column&lt;br /&gt;
- Hiding/showing a column&lt;br /&gt;
- Filtering: The Filter API is used to add filtering functionality to particular columns in the table.&lt;br /&gt;
&lt;br /&gt;
=== Other context data ===&lt;br /&gt;
&lt;br /&gt;
Data not in each record of the queryset, but which needs to be visible on the page. As an example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page. In this case, the project is added to the extra context data for the ToasterTable and accessed in the template.&lt;br /&gt;
&lt;br /&gt;
As an example, we could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17969</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17969"/>
		<updated>2016-04-04T15:08:13Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;*******This is a WIP*******&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render the value from the record into the HTML for the page. The data.completed_on reference in the template demonstrates how to access&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Adding the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
You will also need to define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would be in templates/minibuilds.html.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to modify an existing template (particularly if you are porting a table to ToasterTable). Note that this template extends the base.html template, which provides the header/footer for Toaster itself, but doesn&#039;t have a side menu. For an example of a more complex page, see builds-toastertable.html.&lt;br /&gt;
&lt;br /&gt;
The important things to remember, though, are in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table to render it, passing the name of the template file.&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
== More ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Adding more columns ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Column options&lt;br /&gt;
&lt;br /&gt;
Adding a column to a table:&lt;br /&gt;
- Sorting by a column&lt;br /&gt;
- Hiding/showing a column&lt;br /&gt;
- Filtering: The Filter API is used to add filtering functionality to particular columns in the table.&lt;br /&gt;
&lt;br /&gt;
=== Other context data ===&lt;br /&gt;
&lt;br /&gt;
Data not in each record of the queryset, but which needs to be visible on the page. As an example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page. In this case, the project is added to the extra context data for the ToasterTable and accessed in the template.&lt;br /&gt;
&lt;br /&gt;
As an example, we could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17968</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17968"/>
		<updated>2016-04-04T15:07:57Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This is a WIP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render the value from the record into the HTML for the page. The data.completed_on reference in the template demonstrates how to access&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Adding the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
You will also need to define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would be in templates/minibuilds.html.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to modify an existing template (particularly if you are porting a table to ToasterTable). Note that this template extends the base.html template, which provides the header/footer for Toaster itself, but doesn&#039;t have a side menu. For an example of a more complex page, see builds-toastertable.html.&lt;br /&gt;
&lt;br /&gt;
The important things to remember, though, are in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table to render it, passing the name of the template file.&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
== More ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Adding more columns ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Column options&lt;br /&gt;
&lt;br /&gt;
Adding a column to a table:&lt;br /&gt;
- Sorting by a column&lt;br /&gt;
- Hiding/showing a column&lt;br /&gt;
- Filtering: The Filter API is used to add filtering functionality to particular columns in the table.&lt;br /&gt;
&lt;br /&gt;
=== Other context data ===&lt;br /&gt;
&lt;br /&gt;
Data not in each record of the queryset, but which needs to be visible on the page. As an example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page. In this case, the project is added to the extra context data for the ToasterTable and accessed in the template.&lt;br /&gt;
&lt;br /&gt;
As an example, we could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17967</id>
		<title>ToasterTable</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=ToasterTable&amp;diff=17967"/>
		<updated>2016-04-04T15:07:33Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: Created page with &amp;quot;== Introduction to ToasterTable ==  &amp;#039;&amp;#039;&amp;#039;This is a WIP&amp;#039;&amp;#039;&amp;#039;  ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a s...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction to ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This is a WIP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
ToasterTable is a set of classes in the toastergui module of the Toaster Django application. It is used to present a set of database records in a style and with functionality consistent with the rest of Toaster.&lt;br /&gt;
&lt;br /&gt;
The functionality provided by ToasterTable includes:&lt;br /&gt;
* Sorting&lt;br /&gt;
* Column hiding/showing&lt;br /&gt;
* Filtering&lt;br /&gt;
* Paging&lt;br /&gt;
* Search&lt;br /&gt;
&lt;br /&gt;
When a ToasterTable is updated, a new set of records matching the filter criteria, search string, sort order and page number is fetched from the back-end using Ajax requests. The ToasterTable is then updated in place from the JSON in the response, using JavaScript to redraw the table rows.&lt;br /&gt;
&lt;br /&gt;
As a developer, you don&#039;t need to worry (necessarily) about how this works: you just implement a subclass of ToasterTable, specify a template for rendering it, and supply a URL mapping.&lt;br /&gt;
&lt;br /&gt;
The following sections explain how to use ToasterTable. The filenames in these sections refer to files in the toastergui/ directory.&lt;br /&gt;
&lt;br /&gt;
Note that to be able to follow the example, you will need to set up Toaster for development work. This is described in [http://www.yoctoproject.org/docs/2.0.1/toaster-manual/toaster-manual.html the Toaster manual].&lt;br /&gt;
&lt;br /&gt;
== How to create a ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
Most of the tables needed for Toaster are already implemented. Many of the key tables in Toaster already use ToasterTable, but some don&#039;t. A full list of unconverted tables is in https://bugzilla.yoctoproject.org/show_bug.cgi?id=8363.&lt;br /&gt;
&lt;br /&gt;
This means that you will typically be porting an existing table to ToasterTable, rather than adding a new table from scratch. However, to keep things simple, this section explains how to create a ToasterTable from scratch. Knowing how to do this should make it easier to port an existing table to ToasterTable in future.&lt;br /&gt;
&lt;br /&gt;
To provide a worked example, I&#039;ll create a new table which is a variant of the existing &amp;quot;all builds&amp;quot; table, using a reduced number of columns and filters.&lt;br /&gt;
&lt;br /&gt;
=== Create the table class ===&lt;br /&gt;
&lt;br /&gt;
A quick overview of where things are and where they go when creating a table:&lt;br /&gt;
&lt;br /&gt;
* widgets.py contains the ToasterTable class. This is the base class for all ToasterTables.&lt;br /&gt;
* New tables are added to tables.py.&lt;br /&gt;
* A table in tables.py maps to a URL within the Toaster application; the mapping is defined in urls.py.&lt;br /&gt;
* Templates for the table are added to templates/.&lt;br /&gt;
* If you need [https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/ custom template tags], these go in templatetags/.&lt;br /&gt;
&lt;br /&gt;
To create a new table, first add the class definition to tables.py:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
# import any models you need for the table&lt;br /&gt;
from orm.models import Build&lt;br /&gt;
&lt;br /&gt;
class MiniBuildsTable(ToasterTable):&lt;br /&gt;
    def __init__(self, *args, **kwargs):&lt;br /&gt;
        super(MiniBuildsTable, self).__init__(*args, **kwargs)&lt;br /&gt;
        self.default_orderby = &#039;-completed_on&#039;&lt;br /&gt;
        self.title = &#039;Mini Builds Table&#039;&lt;br /&gt;
&lt;br /&gt;
    def setup_queryset(self, *args, **kwargs):&lt;br /&gt;
        self.queryset = Build.objects.all()&lt;br /&gt;
&lt;br /&gt;
    def setup_columns(self, *args, **kwargs):&lt;br /&gt;
        self.add_column(title=&#039;Completed on&#039;,&lt;br /&gt;
                        help_text=&#039;The date and time when the build finished&#039;,&lt;br /&gt;
                        hideable=False,&lt;br /&gt;
                        orderable=True,&lt;br /&gt;
                        static_data_name=&#039;completed_on&#039;,&lt;br /&gt;
                        static_data_template=&#039;{{data.completed_on | date:&amp;quot;d/m/y H:i&amp;quot;}}&#039;)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The key to creating a ToasterTable is to decide which data is going to be shown in the table and define this in the &#039;&#039;setup_queryset()&#039;&#039; method. This will typically be a queryset derived from one model in the Toaster application. To see the available models, check the orm/models.py file.&lt;br /&gt;
&lt;br /&gt;
Each row in the table corresponds to a record in the queryset. The &#039;&#039;setup_queryset()&#039;&#039; method specifies how to get this queryset for the table: it must set the self.queryset property to the Django queryset containing the data. In the MiniBuildsTable, we show all of the builds by default (using the standard Django model API).&lt;br /&gt;
&lt;br /&gt;
Each column in the ToasterTable corresponds to a field in the queryset. Inside a table row, a cell corresponds to one or more fields from each record in the queryset. A cell can show various aspects of a record: a formatted version of a field&#039;s value, an amalgam of multiple fields, a computation based on a group of related records etc. For example, in a row of the MiniBuildsTable, a cell might contain the outcome of the build, the number of tasks which failed during the build, or the time since the build completed.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;setup_columns()&#039;&#039; method defines which columns should be shown in the table. In this initial version of MiniBuildsTable, only the &amp;quot;completed_on&amp;quot; column is shown. This column has the following properties:&lt;br /&gt;
* It cannot be hidden (hideable=False).&lt;br /&gt;
* It can be used to order the table (orderable=True).&lt;br /&gt;
* static_data_name sets the name of the column so that it can be married up with the sort order specified in the querystring, and with the default_orderby for the ToasterTable (see below).&lt;br /&gt;
* static_data_template specifies how to render the value from the record into the HTML for the page. The data.completed_on reference in the template demonstrates how to access&lt;br /&gt;
&lt;br /&gt;
The self.default_orderby member set in __init__() specifies the default column to use for ordering the table. In this case, the completed_on field is used to sort the builds in the table; the &amp;quot;-&amp;quot; specifies reverse order, so the newest build appears at the top of the table. Note that the value for self.default_orderby should match the name of a field in the model, and the name of a column added to the table.&lt;br /&gt;
&lt;br /&gt;
See the [adding more columns] section for full details of the column options available.&lt;br /&gt;
&lt;br /&gt;
=== Adding the template ===&lt;br /&gt;
&lt;br /&gt;
This should go into templates/, as this is where Django looks for view templates.&lt;br /&gt;
&lt;br /&gt;
In all cases, you will need to include the toastertable.html template from your template. You will also need to add links for the jQuery UI CSS and JS files. (In an ideal world, this would be in toastertable.html, but for historical reasons, it isn&#039;t yet.)&lt;br /&gt;
&lt;br /&gt;
You will also need to define an xhr_table_url variable in your template. This is used to request new data for the table when filters or search terms are applied, or if a new page is requested. ToasterTable will send a request for the table JSON to this URL, then automatically refresh the table display with the new data when it&#039;s received.&lt;br /&gt;
&lt;br /&gt;
As an example, here&#039;s a minimal template for the Mini Builds page, which would be in templates/minibuilds.html.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{% extends &#039;base.html&#039; %}&lt;br /&gt;
{% load static %}&lt;br /&gt;
&lt;br /&gt;
{% block extraheadcontent %}&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.structure.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;{% static &#039;css/jquery-ui.theme.min.css&#039; %}&amp;quot; type=&#039;text/css&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;{% static &#039;js/jquery-ui.min.js&#039; %}&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block title %}{{title}}{% endblock %}&lt;br /&gt;
&lt;br /&gt;
{% block pagecontent %}&lt;br /&gt;
  &amp;lt;h1 class=&amp;quot;page-header top-air&amp;quot;&amp;gt;{{title}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
  {% url &#039;minibuilds&#039; as xhr_table_url %}&lt;br /&gt;
  {% include &#039;toastertable.html&#039; %}&lt;br /&gt;
{% endblock %}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Depending on which side menus or other wrapping you need around your table, you may need to modify an existing template (particularly if you are porting a table to ToasterTable). Note that this template extends the base.html template, which provides the header/footer for Toaster itself, but doesn&#039;t have a side menu. For an example of a more complex page, see builds-toastertable.html.&lt;br /&gt;
&lt;br /&gt;
The important things to remember, though, are in the example template above; in particular, the need to import the jQuery UI assets, set xhr_table_url, and include the toastertable.html template.&lt;br /&gt;
&lt;br /&gt;
=== Add a URL mapping for the table ===&lt;br /&gt;
&lt;br /&gt;
To be able to view the Mini Builds page, it needs a mapping in the urls.py file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
urlpatterns = patterns(&#039;toastergui.views&#039;,&lt;br /&gt;
    // ... other mappings ...&lt;br /&gt;
&lt;br /&gt;
    url(r&#039;^minibuilds/$&#039;,&lt;br /&gt;
        tables.MiniBuildsTable.as_view(template_name=&#039;minibuilds.html&#039;),&lt;br /&gt;
        name=&#039;minibuilds&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is standard Django template mapping code. The only wrinkle is that because a ToasterTable is a Django TemplateView subclass, we call Django&#039;s as_view() method on our table to render it, passing the name of the template file.&lt;br /&gt;
&lt;br /&gt;
Now, with Toaster running locally, you should be able to visit&lt;br /&gt;
&lt;br /&gt;
http://localhost:8000/minibuilds/&lt;br /&gt;
&lt;br /&gt;
in a browser and see your Mini Builds table (note that it will be empty unless you&#039;ve run some builds).&lt;br /&gt;
&lt;br /&gt;
== More ToasterTable ==&lt;br /&gt;
&lt;br /&gt;
The following sections flesh out the options for adding columns to a ToasterTable, adding more data to the page, and using the table filter APIs.&lt;br /&gt;
&lt;br /&gt;
=== Adding more columns ===&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Column options&lt;br /&gt;
&lt;br /&gt;
Adding a column to a table:&lt;br /&gt;
- Sorting by a column&lt;br /&gt;
- Hiding/showing a column&lt;br /&gt;
- Filtering: The Filter API is used to add filtering functionality to particular columns in the table.&lt;br /&gt;
&lt;br /&gt;
=== Other context data ===&lt;br /&gt;
&lt;br /&gt;
Data not in each record of the queryset, but which needs to be visible on the page. As an example, the project builds page shows all of the builds for a project; but the project name needs to be shown at the top of the page. In this case, the project is added to the extra context data for the ToasterTable and accessed in the template.&lt;br /&gt;
&lt;br /&gt;
As an example, we could compute the total number of builds Toaster has recorded and show this in the title at the top of the Mini Builds page.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
Note that if you want this value to update as the table is filtered, you&#039;ll need to do it in JavaScript. This is because the page for a ToasterTable isn&#039;t re-rendered when its data changes: only the HTML for the table is updated. Any additional updates required on the page have to be done similarly, via JavaScript.&lt;br /&gt;
&lt;br /&gt;
For example, to update the title of the page as the table is filtered, we could add this code to the template:&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
(see example for all builds table)&lt;br /&gt;
&lt;br /&gt;
=== Computed and derived column values ===&lt;br /&gt;
&lt;br /&gt;
??? see ProjectsTable in tables.py&lt;br /&gt;
&lt;br /&gt;
Prefer to add methods to models rather than compute values on the fly in the template. This is because these values will very often be useful in other contexts; if they are defined in the view in an ad hoc way, they can&#039;t easily be reused.&lt;br /&gt;
&lt;br /&gt;
== Filter API ==&lt;br /&gt;
&lt;br /&gt;
The classes for creating filters are defined in tablefilter.py.&lt;br /&gt;
&lt;br /&gt;
A filter applies to a single column in the table. Any actions on a filter should filter records according to criteria which make sense with respect to that column. For example, you wouldn&#039;t add a filter to the &amp;quot;Completed on&amp;quot; column of the &amp;quot;All Builds&amp;quot; table which filters builds according to whether they failed or succeeded; but you could add a filter which filters the builds based on when they finished.&lt;br /&gt;
&lt;br /&gt;
A filter adds an option to the column heading to filter in/out particular records or to remove filtering altogether.&lt;br /&gt;
&lt;br /&gt;
Each filter has one or more actions associated with it. It also gets an action by default which turns off the filter and shows all the records again.&lt;br /&gt;
&lt;br /&gt;
Each action changes the records shown in its associated ToasterTable, by applying a set of criteria to the query used to fetch records (e.g. show records for a single date, for a date range, or matching/not matching some other criteria related to the column).&lt;br /&gt;
&lt;br /&gt;
How to add a filter to a field&lt;br /&gt;
&lt;br /&gt;
=== Table filters ===&lt;br /&gt;
&lt;br /&gt;
The TableFilter class acts as a container for a group of TableFilterAction objects.&lt;br /&gt;
&lt;br /&gt;
TableFilterAction is the base class for an action associated with a filter. See the next section for the types of filter action already implemented.&lt;br /&gt;
&lt;br /&gt;
=== Table filter actions ===&lt;br /&gt;
&lt;br /&gt;
TableFilterActionToggle&lt;br /&gt;
TableFilterActionDay&lt;br /&gt;
TableFilterActionDateRange&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster&amp;diff=17966</id>
		<title>Toaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster&amp;diff=17966"/>
		<updated>2016-04-04T15:07:15Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Toaster How-to&amp;#039;s */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
[[Toaster]] is a web-based interface to OpenEmbedded and BitBake.&lt;br /&gt;
[[File:Screenshot toaster.png|thumb|Screenshot of Toaster 2.1]]&lt;br /&gt;
General discussion about &#039;&#039;&#039;Toaster&#039;&#039;&#039; happens on a dedicated mailing list: [https://lists.yoctoproject.org/listinfo/toaster https://lists.yoctoproject.org/listinfo/toaster]&lt;br /&gt;
&lt;br /&gt;
=== Using Toaster ===&lt;br /&gt;
&lt;br /&gt;
Toaster can run in various modes and setups.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Local mode&#039;&#039;&#039; - in this mode Toaster is setup for use as a local development tool. It can be used to configure builds or just as a receiver for builds done on the command line with bitbake. You can launch it like this:&lt;br /&gt;
&lt;br /&gt;
 $ source oe-init-build-env&lt;br /&gt;
 $ source toaster start&lt;br /&gt;
&lt;br /&gt;
You then navigate to the link in your browser (e.g. http://localhost:8000) and configure a project. Or start building in the normal way with bitbake via the command line. Toaster will automatically pick up the builds and you will be able to see them on the build dashboard in your browser.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Production mode&#039;&#039;&#039; - All the same functionality as the local mode but with the web server setup as a shared service for multiple developers to use, this sets up Toaster as a wsgi application and [[Setting up a production instance of Toaster|requires additional configuration]].&lt;br /&gt;
&lt;br /&gt;
=== Toaster How-to&#039;s ===&lt;br /&gt;
&lt;br /&gt;
Specific pages with Toaster how-tos are available below.&lt;br /&gt;
&lt;br /&gt;
* [[Contribute to Toaster]]&lt;br /&gt;
* [[Testing Toaster]]&lt;br /&gt;
* [[Setting up a local instance of Toaster]]&lt;br /&gt;
* [[Setting up a production instance of Toaster]] - documentation for Interactive mode&lt;br /&gt;
* [https://www.yoctoproject.org/documentation/toaster-manual-18 How to use the Toaster web interface]&lt;br /&gt;
* [[How to delete information from the Toaster database]]&lt;br /&gt;
* [[How to  support permission management in Build Mode for Toaster]]&lt;br /&gt;
* [[ToasterTable]]: Toaster&#039;s internal API for adding sortable, filterable, paged tables&lt;br /&gt;
&lt;br /&gt;
=== About Toaster ===&lt;br /&gt;
[[File:Screenshot toaster analyis.png|thumb|Analysis of builds using Toaster]]&lt;br /&gt;
&lt;br /&gt;
* [[File:Working_with_design.pdf]]&lt;br /&gt;
* [https://bugzilla.yoctoproject.org/buglist.cgi?list_id=213820&amp;amp;columnlist=status_whiteboard%2Cassigned_to%2Ctarget_milestone%2Cbug_status%2Cshort_desc%2Cbug_severity%2Cpriority&amp;amp;classification=Build%20System%20%26%20Metadata&amp;amp;query_based_on=Toaster-Opens&amp;amp;query_format=advanced&amp;amp;bug_status=NEW&amp;amp;bug_status=ACCEPTED&amp;amp;bug_status=IN%20PROGRESS%20DESIGN&amp;amp;bug_status=IN%20PROGRESS%20DESIGN%20COMPLETE&amp;amp;bug_status=IN%20PROGRESS%20IMPLEMENTATION&amp;amp;bug_status=REOPENED&amp;amp;bug_status=NEEDINFO&amp;amp;bug_status=WaitForUpstream&amp;amp;component=toaster&amp;amp;product=Toaster&amp;amp;known_name=Toaster-Opens Bug list]&lt;br /&gt;
* [[Toaster architecture design]]&lt;br /&gt;
* [[Toaster and bitbake communications]]&lt;br /&gt;
* [[Toaster testing plan]]&lt;br /&gt;
&lt;br /&gt;
=== In progress documentation ===&lt;br /&gt;
&lt;br /&gt;
We are currently preparing the documentation for the Toaster build functionality. The content here is just a brain dump of what we need to cover (in no particular order). Feel free to add and create content as you see fit:&lt;br /&gt;
&lt;br /&gt;
*[[Using virtualenv]]&lt;br /&gt;
*[[Setting up a production instance of Toaster]]&lt;br /&gt;
*[[manage.py commands]] - this should include an explanation of lsupdates&lt;br /&gt;
*[[Start Toaster in managed mode]]&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=16708</id>
		<title>Toaster and bitbake communications</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=16708"/>
		<updated>2015-11-19T14:55:48Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* How do toasterui + buildinfohelper manage events? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a write up of some investigation I did into how toaster asks bitbake to do stuff, and how it hears about what bitbake is doing. It&#039;s not definitive and not guaranteed to be correct, but it might provide some pointers if you are working on toaster. If anyone knows better, please feel free to correct this.&lt;br /&gt;
&lt;br /&gt;
Any paths given below are relative to the root of the poky/poky-contrib source tree.&lt;br /&gt;
&lt;br /&gt;
== toaster asking bitbake to do stuff ==&lt;br /&gt;
&lt;br /&gt;
First off I wanted to figure out how clicking on the &amp;quot;Build&amp;quot; button in the toaster interface triggers a bitbake build.&lt;br /&gt;
&lt;br /&gt;
toaster only asks bitbake to perform builds when in managed mode. This is the point I started from when doing this investigation. I think analysis mode is very similar, except toaster doesn&#039;t ask bitbake to do anything, it just listens to stuff which is already happening.&lt;br /&gt;
&lt;br /&gt;
=== runbuilds ===&lt;br /&gt;
&lt;br /&gt;
The conversation with bitbake is handled via the runbuilds command (bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py). This command is run in a loop once every second from the toaster start script (bitbake/bin/toaster).&lt;br /&gt;
&lt;br /&gt;
The runbuilds.schedule() function looks for BuildRequests in the database. A &#039;&#039;&#039;BuildRequest&#039;&#039;&#039; is created each time you press a &amp;quot;Build&amp;quot; button in the toaster web interface. Those build requests are created in orm/model.py schedule_build on the project object. When creating this BuildRequest also created are BRLayer for each of the layers in your project, BRTarget for the target to be built and BRBitbake for the version of bitbake to use, these are all defined in bldcontrol/models.py, and contain copies of the information from the project.&lt;br /&gt;
&lt;br /&gt;
The localhostbecontroller takes this BuildRequest&lt;br /&gt;
&lt;br /&gt;
* schedule() gets a localhostbecontroller (be = build environment) instance (there is a becontroller for remote bitbakes, but I don&#039;t think the implementation is complete) and assigns it to a variable bec.&lt;br /&gt;
&lt;br /&gt;
* schedule() then calls bec.triggerBuild() for a build request which is in the BuildRequest.REQ_QUEUED state. Only one build request gets picked up each time the runbuilds script runs: the one with the largest ID.&lt;br /&gt;
&lt;br /&gt;
* schedule() also creates a build identification variable for each build request, combining the primary key of the build request with the primary key of the build environment controller&#039;s build environment (!)).&lt;br /&gt;
&lt;br /&gt;
=== localhostbecontroller ===&lt;br /&gt;
&lt;br /&gt;
This is the build environment controller, which handles instances of build environments. It is also responsible for cloning any layers that are in the build request.&lt;br /&gt;
&lt;br /&gt;
It is a subclass of BuildEnvironmentController.&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() calls getBBController(), which returns a BitBakeController instance (bbctrl). getBBController() actually instantiates the controller if it isn&#039;t already instantiated, passing it a &amp;quot;server&amp;quot; object. This server object is an instance of bb.server.xmlrpc.BitBakeXMLRPCClient().&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() then calls the BitBakeController build() method: bbctrl.build()&lt;br /&gt;
&lt;br /&gt;
=== BitBakeController (bbctrl) ===&lt;br /&gt;
&lt;br /&gt;
This is constructed with the server object, which is an XMLRPC connection to a BitBake server.&lt;br /&gt;
&lt;br /&gt;
* build() invokes the &amp;quot;buildTargets&amp;quot; command on the connection using runCommand().&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCClient (bitbake/lib/bb/server/xmlrpc.py) ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
It has a &amp;quot;connection&amp;quot; member, which has runCommand() invoked on it.&lt;br /&gt;
&lt;br /&gt;
When constructed by getBBController(), initServer() is called as part of its construction. This is a method from BitBakeServer which sets the interface address (default: (localhost,0)) and creates an XMLRPCServer() using this interface address.&lt;br /&gt;
&lt;br /&gt;
Once initServer() is done, establishConnection() is invoked, which creates the &amp;quot;real&amp;quot; socket.&lt;br /&gt;
&lt;br /&gt;
The established connection (which runCommand() is invoked on) is a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeServer ===&lt;br /&gt;
&lt;br /&gt;
BitbakeServer is a subclass of BitBakeBaseServer (declared in bitbake/lib/bb/server/__init__.py); this actually has the addcooker() method which associates a bitbake cooker instance with the connection.&lt;br /&gt;
&lt;br /&gt;
BitBakeBaseServer acts like a decorator around a server implementation; in our case, it&#039;s a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
Calling addcooker() on the BitBakeBaseServer also calls it on any server implementation (serverImpl) which is wrapped by BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCServerConnection ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeBaseServerConnection.&lt;br /&gt;
&lt;br /&gt;
When runCommand() is invoked on this, it&#039;s passed on to the cooker object associated with it, i.e. it actually invokes self.cooker.command.runCommand().&lt;br /&gt;
&lt;br /&gt;
The cooker object is associated with the BitBakeXMLRPCServerConnection when the bitbake/lib/bb/main.py script runs.&lt;br /&gt;
&lt;br /&gt;
=== main.py ===&lt;br /&gt;
&lt;br /&gt;
The main script which starts a bitbake server and ui. This is what runs when you use &amp;quot;bitbake&amp;quot; from the command line.&lt;br /&gt;
&lt;br /&gt;
toaster starts the bitbake server with the --server-only switch, which calls bitbake_main() in this file; this in turn calls the main() function.&lt;br /&gt;
&lt;br /&gt;
This instantiates a bb.cooker.BBCooker and adds it to the server implementation via addcooker(). The cooker is what actually enables commands to be sent to the bitbake server.&lt;br /&gt;
&lt;br /&gt;
=== BBCooker (bitbake/lib/bb/cooker.py) ===&lt;br /&gt;
&lt;br /&gt;
This has a &amp;quot;command&amp;quot; property which is an instance of BBCommand; this is what runCommand() finally gets invoked on.&lt;br /&gt;
&lt;br /&gt;
=== BBCommand ===&lt;br /&gt;
&lt;br /&gt;
runCommand() calls a method from an instance of CommandSync (all the synchronous commands bitbake understands) or CommandAsync (the asynchronous ones), depending on the type of command passed to runCommand(). Both the CommandSync and CommandAsync instances are added to the BBCooker when it is created.&lt;br /&gt;
&lt;br /&gt;
For example, runCommand(&amp;quot;getVariable&amp;quot;, ...) is invoked via CommandsSync.getVariable().&lt;br /&gt;
&lt;br /&gt;
The commands which go through BBCommand.runCommand() now make their way to the bitbake server over XMLRPC.&lt;br /&gt;
&lt;br /&gt;
== toaster listening to what bitbake is doing ==&lt;br /&gt;
&lt;br /&gt;
At this point, I realised I probably understood enough to see how bitbake was being invoked from toaster: asking toaster to start a build sends a &amp;quot;buildTargets&amp;quot; command to the bitbake XMLRPC server, via a rather indirect series of objects and method calls.&lt;br /&gt;
&lt;br /&gt;
What I wanted to know now was how toaster listens to the result of that command. At a high level, I understood that it gathered events from the XMLRPC connection and converted them into database objects. However, I didn&#039;t really get the code path.&lt;br /&gt;
&lt;br /&gt;
I started from BuildInfoHelper, which is where bitbake events are converted into toaster db objects.&lt;br /&gt;
&lt;br /&gt;
=== BuildInfoHelper ===&lt;br /&gt;
&lt;br /&gt;
This is passed build events from the toasterui.py which routes them off to the buildinfohelper based on the event types.&lt;br /&gt;
&lt;br /&gt;
BuildInfoHelper is responsible for constructing toaster ORM objects from events. The BuildInfoHelper is constructed with a server (instance of BitBakeXMLRPCServerConnection in the case of toaster), so it can also interrogate the bitbake server for extra environmental data via getVariable().&lt;br /&gt;
&lt;br /&gt;
BuildInfoHelper also tries to match the data from the build recipe events from toasterui.py to data in toaster&#039;s database, so that toaster can &amp;quot;learn&amp;quot; from the build, this means that we get recipe and package information from the build which otherwise we are unable to determine.&lt;br /&gt;
&lt;br /&gt;
The task of matching the recipes and associated data is done when we get the recipe information from the store task event, this contains a path of the recipe e.g. &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned/mydir/example.bb... the buildinfohelper then tries to find a layer in toaster that has a checkout directory location that starts with the one provided by the event. &lt;br /&gt;
&lt;br /&gt;
It does this by asking the &amp;quot;bc&amp;quot; BuildController (which can only be localhostbecontroller.py at the moment as this is the only controller which contains the implementation) to return the git checkout path via &#039;getGitCloneDirectory&#039; this returns what it believes was the git checkout base location, in our example hopefully something like &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned&amp;quot; then the buildinfohelper adds the directory name (brl.dirpath) from the information in the build request layer, e.g. &amp;quot;mydir&amp;quot; after all this reconstruction we hope that the recipe path we got from the event starts with the reconstructed path. e.g. Does &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned/mydir/example.bb&amp;quot; start with &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned/mydir/&amp;quot; if yes then we have a layer in toaster to which we can associate the information from the build to. &lt;br /&gt;
&lt;br /&gt;
One problem here is that the getGitCloneDirectory function a) doesn&#039;t exist in the other hostcontrollers and b) doesn&#039;t always return the correct value due to some special case&lt;br /&gt;
&lt;br /&gt;
=== toasterui.py ===&lt;br /&gt;
&lt;br /&gt;
This has a main() function which sets up an event listener loop.&lt;br /&gt;
&lt;br /&gt;
main() is passed a server, eventHandler and params. The eventHandler is the object which listens to bitbake events.&lt;br /&gt;
&lt;br /&gt;
toasterui is called from main.py (see below).&lt;br /&gt;
&lt;br /&gt;
=== main.py (bitbake/lib/bb/main.py) ===&lt;br /&gt;
&lt;br /&gt;
The toasterui.py main() method is invoked from main.py.&lt;br /&gt;
&lt;br /&gt;
It&#039;s passed two parameters:&lt;br /&gt;
&lt;br /&gt;
# server_connection.connection&lt;br /&gt;
# server_connection.events&lt;br /&gt;
&lt;br /&gt;
The second of these is the eventHandler which is set up in a loop in toasterui.py.&lt;br /&gt;
&lt;br /&gt;
server_connection comes from a call to establishConnection() in main.py. The server is constructed via a servermodule, which is dynamically chosen in main.py according to the parameters used to invoke it (-t). It will either be &amp;quot;process&amp;quot; or &amp;quot;xmlrpc&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The UI type is set in the main.py script via the -u option.&lt;br /&gt;
&lt;br /&gt;
toaster invokes bitbake with: -t xmlrpc -u toasterui&lt;br /&gt;
&lt;br /&gt;
which means that we get the toasterui.main() called, and we get an xmlrpc bitbake server.&lt;br /&gt;
&lt;br /&gt;
Back to server_connection.events...&lt;br /&gt;
&lt;br /&gt;
This refers to an xmlrpc bitbake server connection&#039;s events object.&lt;br /&gt;
&lt;br /&gt;
So toasterui.main() gets:&lt;br /&gt;
&lt;br /&gt;
# server_connection.connection =&amp;gt; BitBakeXMLRPCServerConnection.connection&lt;br /&gt;
# server_connection.events =&amp;gt; BitBakeXMLRPCServerConnection.events&lt;br /&gt;
&lt;br /&gt;
The events object is an instance of uievent.BBUIEventQueue in our case, as we&#039;re using toasterui.&lt;br /&gt;
&lt;br /&gt;
=== uievent.BBUIEventQueue (bitbake/lib/bb/ui/uievent.py) ===&lt;br /&gt;
&lt;br /&gt;
This is our event handler (&amp;quot;events&amp;quot;) in toasterui.main(), which is receiving events on the XMLRPC connection (see later).&lt;br /&gt;
&lt;br /&gt;
toasterui.main() calls events.waitEvent(0.25), which looks for events on the queue every 0.25s. If the queue has events, one is popped off.&lt;br /&gt;
&lt;br /&gt;
Each event popped from the queue is passed to uihelper.BBUIHelper.eventHandler(), which adds build tracking information (how many packages built, tasks completed etc.)&lt;br /&gt;
&lt;br /&gt;
The event then goes to the BuildInfoHelper, where it gets stored in toaster&#039;s database.&lt;br /&gt;
&lt;br /&gt;
==== How do events get on BBUIEventQueue? ====&lt;br /&gt;
&lt;br /&gt;
BBUIEventQueue is instantiated on the BitBakeXMLRPCServerConnection object.&lt;br /&gt;
&lt;br /&gt;
It is passed a BBServer, which is an xmlrpclib.ServerProxy (from the Python standard library).&lt;br /&gt;
&lt;br /&gt;
The ServerProxy has a method corresponding to each method on the server it is proxying for; in this case, the proxied server is a BBServer, so the proxy has the same methods as BBServer.&lt;br /&gt;
&lt;br /&gt;
Event handling is set up by calling self.BBServer.registerEventHandler() from BBUIEventQueue.&lt;br /&gt;
&lt;br /&gt;
registerEventHandler() is defined in bitbake/lib/bb/server/xmlrpc.py, BitBakeServerCommands. This in turn calls bb.event.register_UIHandler, defined in bitbake/lib/bb/event.py.&lt;br /&gt;
&lt;br /&gt;
When an event is fired, each handler registered for it is invoked with that event (in event.py).&lt;br /&gt;
&lt;br /&gt;
The main function for firing events in event.py is fire_from_worker(), which is called from bitbake/lib/bb/runqueue.py.&lt;br /&gt;
&lt;br /&gt;
Events are constructed from xmlrpc messages coming from the bitbake server (see runqueue.py, runQueuePipe.read()).&lt;br /&gt;
&lt;br /&gt;
==== How do toasterui + buildinfohelper manage events? ====&lt;br /&gt;
&lt;br /&gt;
toasterui runs as part of the bitbake instance: bitbake is invoked with a -u toasterui option, which means that toasterui is instantiated as the event handler for the bitbake instance.&lt;br /&gt;
&lt;br /&gt;
As events occur in bitbake, they are passed to toasterui; toasterui then hands those events off to buildinfohelper, which uses the event data to create records in the Toaster database. Note that buildinfohelper has to start up the Django database machinery manually for this to be possible, and that this is happening outside the main Django instance, in a separate process. This might be part of the reason why we get database locking issues with Django 1.8.&lt;br /&gt;
&lt;br /&gt;
As buildinfohelper receives bitbake events, it sets variables in its internal_state dictionary. These variables are used to represent events which are &amp;quot;partial&amp;quot; from the perspective of Toaster: that is, events which can&#039;t create a complete, useful record in Toaster&#039;s database.&lt;br /&gt;
&lt;br /&gt;
The best example of this comes from bitbake&#039;s Task* events. We receive two events for a task in buildinfohelper:&lt;br /&gt;
&lt;br /&gt;
# The event notifying that the task started, e.g. TaskStarted, runQueueTaskStarted, sceneQueueTaskStarted&lt;br /&gt;
# The end state of the task, e.g. TaskFailedSilent, TaskCompleted, runQueueTaskFailed, sceneQueueTaskFailed, runQueueTaskCompleted, sceneQueueTaskCompleted, runQueueTaskSkipped&lt;br /&gt;
&lt;br /&gt;
Because Toaster represents a Task as a single entity, with an outcome state, we can&#039;t add a complete Task record when a task starts: we add a partial Task record, then update that when the task end event is received.&lt;br /&gt;
&lt;br /&gt;
To get this to work, buildinfohelper saves the partial Task to the database when the task start event is received; then retrieves and updates that record when the task end event arrives. In between these two points, buildinfohelper keeps some internal state about tasks which have started but which don&#039;t have a &amp;quot;done&amp;quot; outcome. However, the identifiers used in this internal state are composed of the task file + name, which is a fairly arbitrary algorithm (I quote: &amp;quot;we do a bit of guessing&amp;quot;). When the task end event is received, it is matched up to the internal state (where we have a list of tasks which haven&#039;t ended) using this fairly arbitrary identifier. This seems like it might be prone to error.&lt;br /&gt;
&lt;br /&gt;
The reason this approach has been used, though, is because (as far as I can tell) bitbake doesn&#039;t provide any identifiers on its events which would enable them to be tied together. A TaskStarted event doesn&#039;t specify which task started (tasks don&#039;t have unique IDs), just the name of that task and its .bb file; there is also nothing to tie a task to the build it&#039;s associated with. This means that any event handler waiting for bitbake events has to manually tie together events and maintain local state to be able to do that.&lt;br /&gt;
&lt;br /&gt;
The following internal state is maintained in buildinfohelper for this purpose:&lt;br /&gt;
&lt;br /&gt;
* lvs (layer versions): layer versions known to Toaster&lt;br /&gt;
* recipes: recipes known to Toaster&lt;br /&gt;
* backlog: list of log events which haven&#039;t been saved for the current build yet&lt;br /&gt;
* brbe: the build request and the build environment primary keys, concatenated together around a colon (e.g. &amp;quot;1:2&amp;quot;)&lt;br /&gt;
* build: the ongoing build (there&#039;s an implicit assumption that bitbake can only run one build at a time)&lt;br /&gt;
* taskdata: a list of ongoing tasks (i.e. task events for which we have received a task started event, but haven&#039;t yet received a task ended event); as task end events are received, the task goes into the database and is removed from taskdata&lt;br /&gt;
* targets: targets for the current build&lt;br /&gt;
* task_order: a counter which is added to each task as it is saved to the database, which marks the order in which the task events were received; it gets incremented each time a certain type of task event is received&lt;br /&gt;
&lt;br /&gt;
Note that lvs and recipes are set before the build started event, using metadata events fired by bitbake.&lt;br /&gt;
&lt;br /&gt;
While the build is ongoing, the internal state affects how data is added to the database: it is used to implicitly associate events with the build which is assumed to occur between a BuildStarted event and the next BuildCompleted/BuildFailed event.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of the workflow to make it clearer:&lt;br /&gt;
&lt;br /&gt;
# toasterui receives a BuildStarted event and passes it to buildinfohelper.&lt;br /&gt;
# buildinfohelper creates a database record B for the new build, and stores it as a property on itself.&lt;br /&gt;
# toasterui receives a TaskStarted event with name &amp;quot;foo&amp;quot; and file &amp;quot;/bar/bar/humbug&amp;quot;; it passes the event to buildinfohelper.&lt;br /&gt;
# buildinfohelper stores the partial task in the database as T, associating it with the build record B; it also stores T under the key &amp;quot;/bar/bar/humbug:foo&amp;quot; in taskdata.&lt;br /&gt;
# toasterui receives a TaskCompleted event with name &amp;quot;foo&amp;quot; and file &amp;quot;/bar/bar/humbug&amp;quot;; it passes the event to buildinfohelper.&lt;br /&gt;
# buildinfohelper marries up the new TaskCompleted event with the partial record T, by matching the new event&#039;s key &amp;quot;/bar/bar/humbug:foo&amp;quot; with the key for the TaskStarted event which is already in taskdata (see 4); T is updated in the database.&lt;br /&gt;
# toasterui receives a BuildCompleted event and passes it to buildinfohelper.&lt;br /&gt;
# buildinfohelper assumes that the BuildCompleted event applies to the build B already stored in its internal state. It updates the database record for B with data about when the build ended, build artifacts etc.&lt;br /&gt;
# toasterui creates a new buildinfohelper, ready to deal with the next build.&lt;br /&gt;
&lt;br /&gt;
backlog is a list of log events which haven&#039;t yet been attached to a build; if a log event occurs before the build has been saved, it is added to the list; then, once a BuildStarted event has occurred, the list of events in the backlog is added to the database and associated with that build.&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
At this point, I felt I understood enough about how events are processed for my purpose, so I didn&#039;t dig any further. I knew where I could amend an event to add/remove properties on it, which is what I was after.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=16707</id>
		<title>Toaster and bitbake communications</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=16707"/>
		<updated>2015-11-19T14:55:09Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* How do events get on BBUIEventQueue? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a write up of some investigation I did into how toaster asks bitbake to do stuff, and how it hears about what bitbake is doing. It&#039;s not definitive and not guaranteed to be correct, but it might provide some pointers if you are working on toaster. If anyone knows better, please feel free to correct this.&lt;br /&gt;
&lt;br /&gt;
Any paths given below are relative to the root of the poky/poky-contrib source tree.&lt;br /&gt;
&lt;br /&gt;
== toaster asking bitbake to do stuff ==&lt;br /&gt;
&lt;br /&gt;
First off I wanted to figure out how clicking on the &amp;quot;Build&amp;quot; button in the toaster interface triggers a bitbake build.&lt;br /&gt;
&lt;br /&gt;
toaster only asks bitbake to perform builds when in managed mode. This is the point I started from when doing this investigation. I think analysis mode is very similar, except toaster doesn&#039;t ask bitbake to do anything, it just listens to stuff which is already happening.&lt;br /&gt;
&lt;br /&gt;
=== runbuilds ===&lt;br /&gt;
&lt;br /&gt;
The conversation with bitbake is handled via the runbuilds command (bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py). This command is run in a loop once every second from the toaster start script (bitbake/bin/toaster).&lt;br /&gt;
&lt;br /&gt;
The runbuilds.schedule() function looks for BuildRequests in the database. A &#039;&#039;&#039;BuildRequest&#039;&#039;&#039; is created each time you press a &amp;quot;Build&amp;quot; button in the toaster web interface. Those build requests are created in orm/model.py schedule_build on the project object. When creating this BuildRequest also created are BRLayer for each of the layers in your project, BRTarget for the target to be built and BRBitbake for the version of bitbake to use, these are all defined in bldcontrol/models.py, and contain copies of the information from the project.&lt;br /&gt;
&lt;br /&gt;
The localhostbecontroller takes this BuildRequest&lt;br /&gt;
&lt;br /&gt;
* schedule() gets a localhostbecontroller (be = build environment) instance (there is a becontroller for remote bitbakes, but I don&#039;t think the implementation is complete) and assigns it to a variable bec.&lt;br /&gt;
&lt;br /&gt;
* schedule() then calls bec.triggerBuild() for a build request which is in the BuildRequest.REQ_QUEUED state. Only one build request gets picked up each time the runbuilds script runs: the one with the largest ID.&lt;br /&gt;
&lt;br /&gt;
* schedule() also creates a build identification variable for each build request, combining the primary key of the build request with the primary key of the build environment controller&#039;s build environment (!)).&lt;br /&gt;
&lt;br /&gt;
=== localhostbecontroller ===&lt;br /&gt;
&lt;br /&gt;
This is the build environment controller, which handles instances of build environments. It is also responsible for cloning any layers that are in the build request.&lt;br /&gt;
&lt;br /&gt;
It is a subclass of BuildEnvironmentController.&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() calls getBBController(), which returns a BitBakeController instance (bbctrl). getBBController() actually instantiates the controller if it isn&#039;t already instantiated, passing it a &amp;quot;server&amp;quot; object. This server object is an instance of bb.server.xmlrpc.BitBakeXMLRPCClient().&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() then calls the BitBakeController build() method: bbctrl.build()&lt;br /&gt;
&lt;br /&gt;
=== BitBakeController (bbctrl) ===&lt;br /&gt;
&lt;br /&gt;
This is constructed with the server object, which is an XMLRPC connection to a BitBake server.&lt;br /&gt;
&lt;br /&gt;
* build() invokes the &amp;quot;buildTargets&amp;quot; command on the connection using runCommand().&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCClient (bitbake/lib/bb/server/xmlrpc.py) ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
It has a &amp;quot;connection&amp;quot; member, which has runCommand() invoked on it.&lt;br /&gt;
&lt;br /&gt;
When constructed by getBBController(), initServer() is called as part of its construction. This is a method from BitBakeServer which sets the interface address (default: (localhost,0)) and creates an XMLRPCServer() using this interface address.&lt;br /&gt;
&lt;br /&gt;
Once initServer() is done, establishConnection() is invoked, which creates the &amp;quot;real&amp;quot; socket.&lt;br /&gt;
&lt;br /&gt;
The established connection (which runCommand() is invoked on) is a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeServer ===&lt;br /&gt;
&lt;br /&gt;
BitbakeServer is a subclass of BitBakeBaseServer (declared in bitbake/lib/bb/server/__init__.py); this actually has the addcooker() method which associates a bitbake cooker instance with the connection.&lt;br /&gt;
&lt;br /&gt;
BitBakeBaseServer acts like a decorator around a server implementation; in our case, it&#039;s a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
Calling addcooker() on the BitBakeBaseServer also calls it on any server implementation (serverImpl) which is wrapped by BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCServerConnection ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeBaseServerConnection.&lt;br /&gt;
&lt;br /&gt;
When runCommand() is invoked on this, it&#039;s passed on to the cooker object associated with it, i.e. it actually invokes self.cooker.command.runCommand().&lt;br /&gt;
&lt;br /&gt;
The cooker object is associated with the BitBakeXMLRPCServerConnection when the bitbake/lib/bb/main.py script runs.&lt;br /&gt;
&lt;br /&gt;
=== main.py ===&lt;br /&gt;
&lt;br /&gt;
The main script which starts a bitbake server and ui. This is what runs when you use &amp;quot;bitbake&amp;quot; from the command line.&lt;br /&gt;
&lt;br /&gt;
toaster starts the bitbake server with the --server-only switch, which calls bitbake_main() in this file; this in turn calls the main() function.&lt;br /&gt;
&lt;br /&gt;
This instantiates a bb.cooker.BBCooker and adds it to the server implementation via addcooker(). The cooker is what actually enables commands to be sent to the bitbake server.&lt;br /&gt;
&lt;br /&gt;
=== BBCooker (bitbake/lib/bb/cooker.py) ===&lt;br /&gt;
&lt;br /&gt;
This has a &amp;quot;command&amp;quot; property which is an instance of BBCommand; this is what runCommand() finally gets invoked on.&lt;br /&gt;
&lt;br /&gt;
=== BBCommand ===&lt;br /&gt;
&lt;br /&gt;
runCommand() calls a method from an instance of CommandSync (all the synchronous commands bitbake understands) or CommandAsync (the asynchronous ones), depending on the type of command passed to runCommand(). Both the CommandSync and CommandAsync instances are added to the BBCooker when it is created.&lt;br /&gt;
&lt;br /&gt;
For example, runCommand(&amp;quot;getVariable&amp;quot;, ...) is invoked via CommandsSync.getVariable().&lt;br /&gt;
&lt;br /&gt;
The commands which go through BBCommand.runCommand() now make their way to the bitbake server over XMLRPC.&lt;br /&gt;
&lt;br /&gt;
== toaster listening to what bitbake is doing ==&lt;br /&gt;
&lt;br /&gt;
At this point, I realised I probably understood enough to see how bitbake was being invoked from toaster: asking toaster to start a build sends a &amp;quot;buildTargets&amp;quot; command to the bitbake XMLRPC server, via a rather indirect series of objects and method calls.&lt;br /&gt;
&lt;br /&gt;
What I wanted to know now was how toaster listens to the result of that command. At a high level, I understood that it gathered events from the XMLRPC connection and converted them into database objects. However, I didn&#039;t really get the code path.&lt;br /&gt;
&lt;br /&gt;
I started from BuildInfoHelper, which is where bitbake events are converted into toaster db objects.&lt;br /&gt;
&lt;br /&gt;
=== BuildInfoHelper ===&lt;br /&gt;
&lt;br /&gt;
This is passed build events from the toasterui.py which routes them off to the buildinfohelper based on the event types.&lt;br /&gt;
&lt;br /&gt;
BuildInfoHelper is responsible for constructing toaster ORM objects from events. The BuildInfoHelper is constructed with a server (instance of BitBakeXMLRPCServerConnection in the case of toaster), so it can also interrogate the bitbake server for extra environmental data via getVariable().&lt;br /&gt;
&lt;br /&gt;
BuildInfoHelper also tries to match the data from the build recipe events from toasterui.py to data in toaster&#039;s database, so that toaster can &amp;quot;learn&amp;quot; from the build, this means that we get recipe and package information from the build which otherwise we are unable to determine.&lt;br /&gt;
&lt;br /&gt;
The task of matching the recipes and associated data is done when we get the recipe information from the store task event, this contains a path of the recipe e.g. &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned/mydir/example.bb... the buildinfohelper then tries to find a layer in toaster that has a checkout directory location that starts with the one provided by the event. &lt;br /&gt;
&lt;br /&gt;
It does this by asking the &amp;quot;bc&amp;quot; BuildController (which can only be localhostbecontroller.py at the moment as this is the only controller which contains the implementation) to return the git checkout path via &#039;getGitCloneDirectory&#039; this returns what it believes was the git checkout base location, in our example hopefully something like &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned&amp;quot; then the buildinfohelper adds the directory name (brl.dirpath) from the information in the build request layer, e.g. &amp;quot;mydir&amp;quot; after all this reconstruction we hope that the recipe path we got from the event starts with the reconstructed path. e.g. Does &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned/mydir/example.bb&amp;quot; start with &amp;quot;/home/yocto/cloned_layers/_mylayer_master.toaster_cloned/mydir/&amp;quot; if yes then we have a layer in toaster to which we can associate the information from the build to. &lt;br /&gt;
&lt;br /&gt;
One problem here is that the getGitCloneDirectory function a) doesn&#039;t exist in the other hostcontrollers and b) doesn&#039;t always return the correct value due to some special case&lt;br /&gt;
&lt;br /&gt;
=== toasterui.py ===&lt;br /&gt;
&lt;br /&gt;
This has a main() function which sets up an event listener loop.&lt;br /&gt;
&lt;br /&gt;
main() is passed a server, eventHandler and params. The eventHandler is the object which listens to bitbake events.&lt;br /&gt;
&lt;br /&gt;
toasterui is called from main.py (see below).&lt;br /&gt;
&lt;br /&gt;
=== main.py (bitbake/lib/bb/main.py) ===&lt;br /&gt;
&lt;br /&gt;
The toasterui.py main() method is invoked from main.py.&lt;br /&gt;
&lt;br /&gt;
It&#039;s passed two parameters:&lt;br /&gt;
&lt;br /&gt;
# server_connection.connection&lt;br /&gt;
# server_connection.events&lt;br /&gt;
&lt;br /&gt;
The second of these is the eventHandler which is set up in a loop in toasterui.py.&lt;br /&gt;
&lt;br /&gt;
server_connection comes from a call to establishConnection() in main.py. The server is constructed via a servermodule, which is dynamically chosen in main.py according to the parameters used to invoke it (-t). It will either be &amp;quot;process&amp;quot; or &amp;quot;xmlrpc&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The UI type is set in the main.py script via the -u option.&lt;br /&gt;
&lt;br /&gt;
toaster invokes bitbake with: -t xmlrpc -u toasterui&lt;br /&gt;
&lt;br /&gt;
which means that we get the toasterui.main() called, and we get an xmlrpc bitbake server.&lt;br /&gt;
&lt;br /&gt;
Back to server_connection.events...&lt;br /&gt;
&lt;br /&gt;
This refers to an xmlrpc bitbake server connection&#039;s events object.&lt;br /&gt;
&lt;br /&gt;
So toasterui.main() gets:&lt;br /&gt;
&lt;br /&gt;
# server_connection.connection =&amp;gt; BitBakeXMLRPCServerConnection.connection&lt;br /&gt;
# server_connection.events =&amp;gt; BitBakeXMLRPCServerConnection.events&lt;br /&gt;
&lt;br /&gt;
The events object is an instance of uievent.BBUIEventQueue in our case, as we&#039;re using toasterui.&lt;br /&gt;
&lt;br /&gt;
=== uievent.BBUIEventQueue (bitbake/lib/bb/ui/uievent.py) ===&lt;br /&gt;
&lt;br /&gt;
This is our event handler (&amp;quot;events&amp;quot;) in toasterui.main(), which is receiving events on the XMLRPC connection (see later).&lt;br /&gt;
&lt;br /&gt;
toasterui.main() calls events.waitEvent(0.25), which looks for events on the queue every 0.25s. If the queue has events, one is popped off.&lt;br /&gt;
&lt;br /&gt;
Each event popped from the queue is passed to uihelper.BBUIHelper.eventHandler(), which adds build tracking information (how many packages built, tasks completed etc.)&lt;br /&gt;
&lt;br /&gt;
The event then goes to the BuildInfoHelper, where it gets stored in toaster&#039;s database.&lt;br /&gt;
&lt;br /&gt;
==== How do events get on BBUIEventQueue? ====&lt;br /&gt;
&lt;br /&gt;
BBUIEventQueue is instantiated on the BitBakeXMLRPCServerConnection object.&lt;br /&gt;
&lt;br /&gt;
It is passed a BBServer, which is an xmlrpclib.ServerProxy (from the Python standard library).&lt;br /&gt;
&lt;br /&gt;
The ServerProxy has a method corresponding to each method on the server it is proxying for; in this case, the proxied server is a BBServer, so the proxy has the same methods as BBServer.&lt;br /&gt;
&lt;br /&gt;
Event handling is set up by calling self.BBServer.registerEventHandler() from BBUIEventQueue.&lt;br /&gt;
&lt;br /&gt;
registerEventHandler() is defined in bitbake/lib/bb/server/xmlrpc.py, BitBakeServerCommands. This in turn calls bb.event.register_UIHandler, defined in bitbake/lib/bb/event.py.&lt;br /&gt;
&lt;br /&gt;
When an event is fired, each handler registered for it is invoked with that event (in event.py).&lt;br /&gt;
&lt;br /&gt;
The main function for firing events in event.py is fire_from_worker(), which is called from bitbake/lib/bb/runqueue.py.&lt;br /&gt;
&lt;br /&gt;
Events are constructed from xmlrpc messages coming from the bitbake server (see runqueue.py, runQueuePipe.read()).&lt;br /&gt;
&lt;br /&gt;
==== How do toasterui + buildinfohelper manage events? ====&lt;br /&gt;
&lt;br /&gt;
toasterui runs as part of the bitbake instance: bitbake is invoked with a -u toasterui option, which means that toasterui is instantiated as the event handler for the bitbake instance.&lt;br /&gt;
&lt;br /&gt;
As events occur in bitbake, they are passed to toasterui; toasterui then hands those events off to buildinfohelper, which uses the event data to create records in the Toaster database. Note that buildinfohelper has to start up the Django database machinery manually for this to be possible, and that this is happening outside the main Django instance, in a separate process. This might be part of the reason why we get database locking issues with Django 1.8.&lt;br /&gt;
&lt;br /&gt;
As buildinfohelper receives bitbake events, it sets variables in its internal_state dictionary. These variables are used to represent events which are &amp;quot;partial&amp;quot; from the perspective of Toaster: that is, events which can&#039;t create a complete, useful record in Toaster&#039;s database.&lt;br /&gt;
&lt;br /&gt;
The best example of this comes from bitbake&#039;s Task* events. We receive two events for a task in buildinfohelper:&lt;br /&gt;
&lt;br /&gt;
1. The event notifying that the task started, e.g. TaskStarted, runQueueTaskStarted, sceneQueueTaskStarted&lt;br /&gt;
2. The end state of the task, e.g. TaskFailedSilent, TaskCompleted, runQueueTaskFailed, sceneQueueTaskFailed, runQueueTaskCompleted, sceneQueueTaskCompleted, runQueueTaskSkipped&lt;br /&gt;
&lt;br /&gt;
Because Toaster represents a Task as a single entity, with an outcome state, we can&#039;t add a complete Task record when a task starts: we add a partial Task record, then update that when the task end event is received.&lt;br /&gt;
&lt;br /&gt;
To get this to work, buildinfohelper saves the partial Task to the database when the task start event is received; then retrieves and updates that record when the task end event arrives. In between these two points, buildinfohelper keeps some internal state about tasks which have started but which don&#039;t have a &amp;quot;done&amp;quot; outcome. However, the identifiers used in this internal state are composed of the task file + name, which is a fairly arbitrary algorithm (I quote: &amp;quot;we do a bit of guessing&amp;quot;). When the task end event is received, it is matched up to the internal state (where we have a list of tasks which haven&#039;t ended) using this fairly arbitrary identifier. This seems like it might be prone to error.&lt;br /&gt;
&lt;br /&gt;
The reason this approach has been used, though, is because (as far as I can tell) bitbake doesn&#039;t provide any identifiers on its events which would enable them to be tied together. A TaskStarted event doesn&#039;t specify which task started (tasks don&#039;t have unique IDs), just the name of that task and its .bb file; there is also nothing to tie a task to the build it&#039;s associated with. This means that any event handler waiting for bitbake events has to manually tie together events and maintain local state to be able to do that.&lt;br /&gt;
&lt;br /&gt;
The following internal state is maintained in buildinfohelper for this purpose:&lt;br /&gt;
&lt;br /&gt;
* lvs (layer versions): layer versions known to Toaster&lt;br /&gt;
* recipes: recipes known to Toaster&lt;br /&gt;
* backlog: list of log events which haven&#039;t been saved for the current build yet&lt;br /&gt;
* brbe: the build request and the build environment primary keys, concatenated together around a colon (e.g. &amp;quot;1:2&amp;quot;)&lt;br /&gt;
* build: the ongoing build (there&#039;s an implicit assumption that bitbake can only run one build at a time)&lt;br /&gt;
* taskdata: a list of ongoing tasks (i.e. task events for which we have received a task started event, but haven&#039;t yet received a task ended event); as task end events are received, the task goes into the database and is removed from taskdata&lt;br /&gt;
* targets: targets for the current build&lt;br /&gt;
* task_order: a counter which is added to each task as it is saved to the database, which marks the order in which the task events were received; it gets incremented each time a certain type of task event is received&lt;br /&gt;
&lt;br /&gt;
Note that lvs and recipes are set before the build started event, using metadata events fired by bitbake.&lt;br /&gt;
&lt;br /&gt;
While the build is ongoing, the internal state affects how data is added to the database: it is used to implicitly associate events with the build which is assumed to occur between a BuildStarted event and the next BuildCompleted/BuildFailed event.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of the workflow to make it clearer:&lt;br /&gt;
&lt;br /&gt;
1. toasterui receives a BuildStarted event and passes it to buildinfohelper.&lt;br /&gt;
2. buildinfohelper creates a database record B for the new build, and stores it as a property on itself.&lt;br /&gt;
3. toasterui receives a TaskStarted event with name &amp;quot;foo&amp;quot; and file &amp;quot;/bar/bar/humbug&amp;quot;; it passes the event to buildinfohelper.&lt;br /&gt;
4. buildinfohelper stores the partial task in the database as T, associating it with the build record B; it also stores T under the key &amp;quot;/bar/bar/humbug:foo&amp;quot; in taskdata.&lt;br /&gt;
5. toasterui receives a TaskCompleted event with name &amp;quot;foo&amp;quot; and file &amp;quot;/bar/bar/humbug&amp;quot;; it passes the event to buildinfohelper.&lt;br /&gt;
6. buildinfohelper marries up the new TaskCompleted event with the partial record T, by matching the new event&#039;s key &amp;quot;/bar/bar/humbug:foo&amp;quot; with the key for the TaskStarted event which is already in taskdata (see 4); T is updated in the database.&lt;br /&gt;
7. toasterui receives a BuildCompleted event and passes it to buildinfohelper.&lt;br /&gt;
8. buildinfohelper assumes that the BuildCompleted event applies to the build B already stored in its internal state. It updates the database record for B with data about when the build ended, build artifacts etc.&lt;br /&gt;
9. toasterui creates a new buildinfohelper, ready to deal with the next build.&lt;br /&gt;
&lt;br /&gt;
backlog is a list of log events which haven&#039;t yet been attached to a build; if a log event occurs before the build has been saved, it is added to the list; then, once a BuildStarted event has occurred, the list of events in the backlog is added to the database and associated with that build.&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
At this point, I felt I understood enough about how events are processed for my purpose, so I didn&#039;t dig any further. I knew where I could amend an event to add/remove properties on it, which is what I was after.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Setting_up_a_production_instance_of_Toaster&amp;diff=16622</id>
		<title>Setting up a production instance of Toaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Setting_up_a_production_instance_of_Toaster&amp;diff=16622"/>
		<updated>2015-11-09T11:46:24Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
&lt;br /&gt;
A production instance of Toaster is one in which you wish to share the Toaster instance with remote and multiple users. It is also the setup which can cope with heavier loads on the web service. These instructions setup toaster in Build mode where builds and projects are run, viewed and defined by the Toaster web interface.&lt;br /&gt;
&lt;br /&gt;
== Requirements ==&lt;br /&gt;
* [http://www.yoctoproject.org/docs/2.0/yocto-project-qs/yocto-project-qs.html#packages Build requirements]&lt;br /&gt;
* Apache webserver&lt;br /&gt;
* mod-wsgi for Apache webserver&lt;br /&gt;
* Mysql database server&lt;br /&gt;
&lt;br /&gt;
Ubuntu 14.04.3:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ sudo apt-get install apache2 libapache2-mod-wsgi mysql-server virtualenv libmysqlclient-dev&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fedora 22/RH:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ sudo dnf install httpd mod_wsgi python-virtualenv gcc mysql-devel&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.&#039;&#039;&#039; Checkout a copy of Poky into the web server directory. We&#039;re going to be using /var/www/toaster.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
  $ mkdir -p /var/www/toaster&lt;br /&gt;
  $ cd /var/www/toaster/&lt;br /&gt;
  $ git clone git://git.yoctoproject.org/poky&lt;br /&gt;
  $ git checkout jethro # change for any release name required&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2.&#039;&#039;&#039; Initialise a virtualenv and install Toaster dependencies. (Use virtualenv to keep the python packages isolated from your system provided packages - not required but recommended, alternative use your OS&#039;s package manager to install the packages)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ cd /var/www/toaster/&lt;br /&gt;
   $ virtualenv venv&lt;br /&gt;
   $ source ./venv/bin/activate&lt;br /&gt;
   $ pip install -r ./poky/bitbake/toaster-requirements.txt&lt;br /&gt;
   $ pip install mysql&lt;br /&gt;
   $ pip install MySQL-python&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3.&#039;&#039;&#039; Configure toaster edit /var/www/toaster/poky/bitbake/lib/toaster/toastermain/settings.py&lt;br /&gt;
&lt;br /&gt;
Edit the DATABASE settings:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 DATABASES = {&lt;br /&gt;
     &#039;default&#039;: {&lt;br /&gt;
         &#039;ENGINE&#039;: &#039;django.db.backends.mysql&#039;, &lt;br /&gt;
         &#039;NAME&#039;: &#039;toaster_data&#039;,                     &lt;br /&gt;
         &#039;USER&#039;: &#039;toaster&#039;,&lt;br /&gt;
         &#039;PASSWORD&#039;: &#039;yourpasswordhere&#039;,&lt;br /&gt;
         &#039;HOST&#039;: &#039;localhost&#039;,                 &lt;br /&gt;
         &#039;PORT&#039;: &#039;3306&#039;,                      &lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit the SECRET_KEY:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 SECRET_KEY = &#039;YOUR SECRET RANDOM KEY HERE&#039;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit the STATIC_ROOT:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 STATIC_ROOT = &#039;/var/www/toaster/static_files/&#039;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.&#039;&#039;&#039;&#039; Now add the database and user to your mysql server that we just defined&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ mysql -u root -p&lt;br /&gt;
 mysql&amp;gt; CREATE DATABASE toaster_data;&lt;br /&gt;
 mysql&amp;gt; CREATE USER &#039;toaster&#039;@&#039;localhost&#039; identified by &#039;yourpasswordhere&#039;;&lt;br /&gt;
 mysql&amp;gt; GRANT all on toaster_data.* to &#039;toaster&#039;@&#039;localhost&#039;;&lt;br /&gt;
 mysql&amp;gt; quit&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
n.b. You may want to decide on fewer [https://dev.mysql.com/doc/refman/5.1/en/grant.html privileges] to the toaster user. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;5.&#039;&#039;&#039; Get toaster to create the database schema, default data and collect up the statically service files&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ cd  /var/www/toaster/poky/&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py syncdb --migrate&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py loadconf ./meta-yocto/conf/toasterconf.json&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py lsupdates&lt;br /&gt;
 $ ./bitbake/lib/toaster/manage.py collectstatic&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;6.&#039;&#039;&#039; Add a config file for Toaster to your Apache web server&#039;s configurations available directory.&lt;br /&gt;
&lt;br /&gt;
Ubuntu/Debian put it here: /etc/apache2/conf-available/toaster.conf&lt;br /&gt;
Fedora/RH usually here: /etc/httpd/conf.d/toaster.conf&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 Alias /static /var/www/toaster/static_files&lt;br /&gt;
 &amp;lt;Directory /var/www/toaster/static_files&amp;gt;&lt;br /&gt;
 	Order allow,deny&lt;br /&gt;
 	Allow from all&lt;br /&gt;
 	Require all granted&lt;br /&gt;
 &amp;lt;/Directory&amp;gt;&lt;br /&gt;
 WSGIDaemonProcess toaster_wsgi python-path=/var/www/toaster/poky/bitbake/lib/toaster:/var/www/toaster/venv/lib/python2.7/site-packages&lt;br /&gt;
 WSGIScriptAlias / &amp;quot;/var/www/toaster/poky/bitbake/lib/toaster/toastermain/wsgi.py&amp;quot;&lt;br /&gt;
 &amp;lt;Location /&amp;gt;&lt;br /&gt;
     WSGIProcessGroup toaster_wsgi&lt;br /&gt;
 &amp;lt;/Location&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Ubuntu/Debain you will need to enable the config and module in Apache webserver&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ sudo a2enmod wsgi&lt;br /&gt;
   $ sudo a2enconf toaster&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Restart Apache web server to make sure all new configuration is loaded&lt;br /&gt;
&lt;br /&gt;
Ubuntu/Debian:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ sudo service apache2 restart&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fedora/RH:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
   $ sudo service httpd restart&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;7.&#039;&#039;&#039; Install the build runner service&lt;br /&gt;
&lt;br /&gt;
This service needs to be running in order to dispatch builds the command that needs to be run is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 /var/www/toaster/poky/bitbake/lib/toaster/manage.py runbuilds&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sample script:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 #!/bin/sh&lt;br /&gt;
 # toaster run builds dispatcher&lt;br /&gt;
 cd /var/www/toaster/&lt;br /&gt;
 source ./venv/bin/activate&lt;br /&gt;
 ./bitbake/lib/toaster/manage.py runbuilds&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
N.b. You may wish to add a service entry to your OS&#039;s init system so that it starts up on start up as well as adding a dedicated user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now open up a browser and you can start using Toaster!&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_future_release_planning&amp;diff=16322</id>
		<title>Toaster future release planning</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_future_release_planning&amp;diff=16322"/>
		<updated>2015-10-21T14:55:22Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Elliot&amp;#039;s */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
&lt;br /&gt;
== 2.1 Planning ==&lt;br /&gt;
&lt;br /&gt;
=== Wish lists ===&lt;br /&gt;
&lt;br /&gt;
==== Michael&#039;s ====&lt;br /&gt;
* Finish image-customisation  8070, 8081, 8082, 8103, 8104, 8117, 8128, 8132, 8091&lt;br /&gt;
* Remove pseudo API cruft / template context to json response stuff&lt;br /&gt;
* Consolidate used API into it&#039;s own view&lt;br /&gt;
* Finish toaster tables porting&lt;br /&gt;
* Get selenium test wrapper&lt;br /&gt;
* Create a toasterclient.py to interact with toaster to avoid need for &#039;command line builds&#039;&lt;br /&gt;
** This would use toaster&#039;s API as the entry point to using bitbake&lt;br /&gt;
** It could be called by CI systems&lt;br /&gt;
** It could be used by auto builders etc&lt;br /&gt;
** Means that there are no two modes anywhere in Toaster&lt;br /&gt;
** Allows project configuration to be changed in toaster from the command line&lt;br /&gt;
&lt;br /&gt;
==== Brian&#039;s ====&lt;br /&gt;
This is *not* ordered! This is more of a brain dump :)&lt;br /&gt;
* Collapse analysis and managed mode&lt;br /&gt;
* Working CI for each commit into toaster-next as well as for project peoples testME branches on poky-contrib:&lt;br /&gt;
** run django tests&lt;br /&gt;
** run selenium tests&lt;br /&gt;
** leverage sstate to make runs generally shorter&lt;br /&gt;
* remove git assumptions from code&lt;br /&gt;
* rationalize/simplify the configuration scripts&lt;br /&gt;
* if bitbake-memres comes out, interact with it gracefully (this is my preferred method of collapsing analysis and managed)&lt;br /&gt;
* fix the tmpdir/release build issue &amp;amp;/or discuss the need to support building old releases with a UI rather than limiting toaster to build the release it is part of. This is inherently fragile.&lt;br /&gt;
* ask bb to parse any layer added to a project and such as imported layers, and layers specified in the configuration file to give us recipe information.&lt;br /&gt;
* improve our build event following:&lt;br /&gt;
** note parse events&lt;br /&gt;
** update/retrieve ui so that we can tell something is happening before the first build event comes &lt;br /&gt;
** eliminate the remaining random tracebacks from try/except failures.&lt;br /&gt;
* allow configuration in addition to the addition/removal of layers/packages&lt;br /&gt;
** kernel config&lt;br /&gt;
* add user control/authentication&lt;br /&gt;
* update to Django 1.8.  Always stay on Django LTS versions&lt;br /&gt;
* add layer update from layers.openembedded.org to the UI&lt;br /&gt;
* add Django tests for building e.g. we should be able to build core-image-sato from a django test and validate the db&lt;br /&gt;
* add mysql script to simplify setup with mysql/apache&lt;br /&gt;
* pull in new look and feel&lt;br /&gt;
* add django tests to validate all artifacts - if we say they can download it and it appears on a page anywhere, it should have a test&lt;br /&gt;
* improve docs of code on wiki. We have a nice start (ty E &amp;amp; M); I&#039;d like to improve it.  &lt;br /&gt;
** events - more info and list types supported&lt;br /&gt;
** what logic is in jscript , what is in python&lt;br /&gt;
** database schema definition and description esp interrelationships.&lt;br /&gt;
** list django tests, mostly so we can see what we are missing. &lt;br /&gt;
* build mode triggerred off of database entry, not autonomous process.&lt;br /&gt;
* add asynchronicity. I have noted a number of places where commands result in a sluggish ui. I&#039;m nervous about this one.&lt;br /&gt;
** for example, starting a build that requires us to clone layers has a noticeable delay as compared to starting a local only project build.&lt;br /&gt;
* verify we can stop builds cleanly.&lt;br /&gt;
* get the rest of our logs into our local toaster dir&lt;br /&gt;
* allow us to run and stop 2 toasters on the same machine without them interfering with each other&lt;br /&gt;
* (2.2 or later for sure) discuss how to support multiple back end bb servers.  &lt;br /&gt;
** probably will need to be able to add servers/remove servers/see server status/build state&lt;br /&gt;
* be able to see more details about build.  For example, see the &amp;quot;jobs&amp;quot; being processed like you can in knotty&lt;br /&gt;
* revisit what variables we let people change. sstate_dir and dl_dir should certainly be allowed to change&lt;br /&gt;
* discuss way we do migrations. possibly change. thoughts:&lt;br /&gt;
** only support migrations from last release to current. (e.g. if you were running toaster jethro you could update to jethro++, but could not from fido to jethro)&lt;br /&gt;
** take migration from the startup script. it&#039;s only for releasetorelease.&lt;br /&gt;
*** allow it to be in for master and ease of development w/in a release and pull at end as we do with moving releases.&lt;br /&gt;
** whatever we decide, add a test set for it so we know it works&lt;br /&gt;
* Be able to delete things from the ui&lt;br /&gt;
** old builds&lt;br /&gt;
** old projects&lt;br /&gt;
** imported layers we no longer want&lt;br /&gt;
* Be able to set from the ui a policy to delete all builds/recipes/packages that are older than XXX &amp;amp;/or be able to set maximum size on db and delete everything older than XXX when we near that size&lt;br /&gt;
* update the builds on the web page so we dont have to hit refresh to see the build progress.&lt;br /&gt;
&lt;br /&gt;
==== Elliot&#039;s ====&lt;br /&gt;
&lt;br /&gt;
* Make sure all files lint properly&lt;br /&gt;
* Include lint in CI builds&lt;br /&gt;
* Improve test coverage of all areas of the UI and back-end&lt;br /&gt;
* Clean up toasterui.py so it&#039;s easier to see what&#039;s going on in there&lt;br /&gt;
* Fix localhostbecontroller so it doesn&#039;t rely on what&#039;s in the log file when figuring out whether the bitbake server has started&lt;br /&gt;
* Provide a way to modify settings in settings.py without editing that file (e.g. with a local overlay configuration file)&lt;br /&gt;
* Move the logic, environment variables, directory setup and script calls from bin/toaster into Django commands: this would allow you to run toaster correctly, with all the required setup, without having to use bin/toaster; it would also mean that we could provide cleaner production setup instructions, as a user could run a series of Django commands (with their own parameters) to setup their environment; bin/toaster would then just execute a series of Django commands&lt;br /&gt;
&lt;br /&gt;
== All bugs for 2.1+ ==&lt;br /&gt;
&lt;br /&gt;
{{#bugzilla:&lt;br /&gt;
  |columns=id,milestone,summary,status,priority&lt;br /&gt;
  |milestone=future,2.1,2.1 M1,2.1 M2,2.1 M3,2.1 M4&lt;br /&gt;
  |sort=priority&lt;br /&gt;
  |product=Toaster&lt;br /&gt;
  |status=!resolved&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[https://bugzilla.yoctoproject.org/buglist.cgi?bug_status=UNCONFIRMED&amp;amp;bug_status=NEW&amp;amp;bug_status=ACCEPTED&amp;amp;bug_status=IN%20PROGRESS%20DESIGN&amp;amp;bug_status=IN%20PROGRESS%20DESIGN%20COMPLETE&amp;amp;bug_status=IN%20PROGRESS%20IMPLEMENTATION&amp;amp;bug_status=IN%20PROGRESS%20REVIEW&amp;amp;bug_status=REOPENED&amp;amp;bug_status=NEEDINFO&amp;amp;bug_status=WaitForUpstream&amp;amp;columnlist=product%2Cbug_status%2Cshort_desc%2Cchangeddate%2Ctarget_milestone&amp;amp;component=toaster&amp;amp;f1=target_milestone&amp;amp;list_id=476868&amp;amp;o1=greaterthan&amp;amp;product=Toaster&amp;amp;query_format=advanced&amp;amp;resolution=---&amp;amp;v1=2.0&amp;amp;query_based_on= Bugzilla query]&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_future_release_planning&amp;diff=16320</id>
		<title>Toaster future release planning</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_future_release_planning&amp;diff=16320"/>
		<updated>2015-10-21T11:35:59Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Wish lists */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
&lt;br /&gt;
== 2.1 Planning ==&lt;br /&gt;
&lt;br /&gt;
=== Wish lists ===&lt;br /&gt;
&lt;br /&gt;
==== Michael&#039;s ====&lt;br /&gt;
* Finish image-customisation  8070, 8081, 8082, 8103, 8104, 8117, 8128, 8132, 8091&lt;br /&gt;
* Remove pseudo API cruft / template context to json response stuff&lt;br /&gt;
* Consolidate used API into it&#039;s own view&lt;br /&gt;
* Finish toaster tables porting&lt;br /&gt;
* Get selenium test wrapper&lt;br /&gt;
* Create a toasterclient.py to interact with toaster to avoid need for &#039;command line builds&#039;&lt;br /&gt;
** This would use toaster&#039;s API as the entry point to using bitbake&lt;br /&gt;
** It could be called by CI systems&lt;br /&gt;
** It could be used by auto builders etc&lt;br /&gt;
** Means that there are no two modes anywhere in Toaster&lt;br /&gt;
** Allows project configuration to be changed in toaster from the command line&lt;br /&gt;
&lt;br /&gt;
==== Brian&#039;s ====&lt;br /&gt;
This is *not* ordered! This is more of a brain dump :)&lt;br /&gt;
* Collapse analysis and managed mode&lt;br /&gt;
* Working CI for each commit into toaster-next as well as for project peoples testME branches on poky-contrib:&lt;br /&gt;
** run django tests&lt;br /&gt;
** run selenium tests&lt;br /&gt;
** leverage sstate to make runs generally shorter&lt;br /&gt;
* remove git assumptions from code&lt;br /&gt;
* rationalize/simplify the configuration scripts&lt;br /&gt;
* if bitbake-memres comes out, interact with it gracefully (this is my preferred method of collapsing analysis and managed)&lt;br /&gt;
* fix the tmpdir/release build issue &amp;amp;/or discuss the need to support building old releases with a UI rather than limiting toaster to build the release it is part of. This is inherently fragile.&lt;br /&gt;
* ask bb to parse any layer added to a project and such as imported layers, and layers specified in the configuration file to give us recipe information.&lt;br /&gt;
* improve our build event following:&lt;br /&gt;
** note parse events&lt;br /&gt;
** update/retrieve ui so that we can tell something is happening before the first build event comes &lt;br /&gt;
** eliminate the remaining random tracebacks from try/except failures.&lt;br /&gt;
* allow configuration in addition to the addition/removal of layers/packages&lt;br /&gt;
** kernel config&lt;br /&gt;
* add user control/authentication&lt;br /&gt;
* update to Django 1.8.  Always stay on Django LTS versions&lt;br /&gt;
* add layer update from layers.openembedded.org to the UI&lt;br /&gt;
* add Django tests for building e.g. we should be able to build core-image-sato from a django test and validate the db&lt;br /&gt;
* add mysql script to simplify setup with mysql/apache&lt;br /&gt;
* pull in new look and feel&lt;br /&gt;
* add django tests to validate all artifacts - if we say they can download it and it appears on a page anywhere, it should have a test&lt;br /&gt;
* improve docs of code on wiki. We have a nice start (ty E &amp;amp; M); I&#039;d like to improve it.  &lt;br /&gt;
** events - more info and list types supported&lt;br /&gt;
** what logic is in jscript , what is in python&lt;br /&gt;
** database schema definition and description esp interrelationships.&lt;br /&gt;
** list django tests, mostly so we can see what we are missing. &lt;br /&gt;
* build mode triggerred off of database entry, not autonomous process.&lt;br /&gt;
* add asynchronicity. I have noted a number of places where commands result in a sluggish ui. I&#039;m nervous about this one.&lt;br /&gt;
** for example, starting a build that requires us to clone layers has a noticeable delay as compared to starting a local only project build.&lt;br /&gt;
* verify we can stop builds cleanly.&lt;br /&gt;
* get the rest of our logs into our local toaster dir&lt;br /&gt;
* allow us to run and stop 2 toasters on the same machine without them interfering with each other&lt;br /&gt;
* (2.2 or later for sure) discuss how to support multiple back end bb servers.  &lt;br /&gt;
** probably will need to be able to add servers/remove servers/see server status/build state&lt;br /&gt;
* be able to see more details about build.  For example, see the &amp;quot;jobs&amp;quot; being processed like you can in knotty&lt;br /&gt;
* revisit what variables we let people change. sstate_dir and dl_dir should certainly be allowed to change&lt;br /&gt;
* discuss way we do migrations. possibly change. thoughts:&lt;br /&gt;
** only support migrations from last release to current. (e.g. if you were running toaster jethro you could update to jethro++, but could not from fido to jethro)&lt;br /&gt;
** take migration from the startup script. it&#039;s only for releasetorelease.&lt;br /&gt;
*** allow it to be in for master and ease of development w/in a release and pull at end as we do with moving releases.&lt;br /&gt;
** whatever we decide, add a test set for it so we know it works&lt;br /&gt;
* Be able to delete things from the ui&lt;br /&gt;
** old builds&lt;br /&gt;
** old projects&lt;br /&gt;
** imported layers we no longer want&lt;br /&gt;
* Be able to set from the ui a policy to delete all builds/recipes/packages that are older than XXX &amp;amp;/or be able to set maximum size on db and delete everything older than XXX when we near that size&lt;br /&gt;
* update the builds on the web page so we dont have to hit refresh to see the build progress.&lt;br /&gt;
&lt;br /&gt;
==== Elliot&#039;s ====&lt;br /&gt;
&lt;br /&gt;
* Make sure all files lint properly&lt;br /&gt;
* Include lint in CI builds&lt;br /&gt;
* Improve test coverage of all areas of the UI and back-end&lt;br /&gt;
* Clean up toasterui.py so it&#039;s easier to see what&#039;s going on in there&lt;br /&gt;
* Fix localhostbecontroller so it doesn&#039;t rely on what&#039;s in the log file when figuring out whether the bitbake server has started&lt;br /&gt;
&lt;br /&gt;
== All bugs for 2.1+ ==&lt;br /&gt;
&lt;br /&gt;
{{#bugzilla:&lt;br /&gt;
  |columns=id,milestone,summary,status,priority&lt;br /&gt;
  |milestone=future,2.1,2.1 M1,2.1 M2,2.1 M3,2.1 M4&lt;br /&gt;
  |sort=priority&lt;br /&gt;
  |product=Toaster&lt;br /&gt;
  |status=!resolved&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[https://bugzilla.yoctoproject.org/buglist.cgi?bug_status=UNCONFIRMED&amp;amp;bug_status=NEW&amp;amp;bug_status=ACCEPTED&amp;amp;bug_status=IN%20PROGRESS%20DESIGN&amp;amp;bug_status=IN%20PROGRESS%20DESIGN%20COMPLETE&amp;amp;bug_status=IN%20PROGRESS%20IMPLEMENTATION&amp;amp;bug_status=IN%20PROGRESS%20REVIEW&amp;amp;bug_status=REOPENED&amp;amp;bug_status=NEEDINFO&amp;amp;bug_status=WaitForUpstream&amp;amp;columnlist=product%2Cbug_status%2Cshort_desc%2Cchangeddate%2Ctarget_milestone&amp;amp;component=toaster&amp;amp;f1=target_milestone&amp;amp;list_id=476868&amp;amp;o1=greaterthan&amp;amp;product=Toaster&amp;amp;query_format=advanced&amp;amp;resolution=---&amp;amp;v1=2.0&amp;amp;query_based_on= Bugzilla query]&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Contribute_to_Toaster&amp;diff=15968</id>
		<title>Contribute to Toaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Contribute_to_Toaster&amp;diff=15968"/>
		<updated>2015-09-24T13:28:55Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Submitting patch sets for integration into Bitbake */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
This page summarises the Toaster development process. We hope this will help you start contributing to the project. &lt;br /&gt;
&lt;br /&gt;
== What can I do? ==&lt;br /&gt;
&lt;br /&gt;
The [https://bugzilla.yoctoproject.org/buglist.cgi?product=Toaster Yocto Project Bugzilla instance] lists all the things that need to be done:&lt;br /&gt;
&lt;br /&gt;
* If the issue says &amp;lt;strong&amp;gt;GUI design available&amp;lt;/strong&amp;gt; in the Whiteboard field, there is a design specification document attached to the issue that you should follow. Send questions / comments about it to the [https://lists.yoctoproject.org/listinfo/toaster Toaster mailing list]&lt;br /&gt;
* If the issue says &amp;lt;strong&amp;gt;GUI design pending&amp;lt;/strong&amp;gt; in the Whiteboard field, there is some design work still to be done. Feel free to take the issue and send an email to the [https://lists.yoctoproject.org/listinfo/toaster Toaster mailing list] to find out why the design work is not done yet&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Set up the local repository ==&lt;br /&gt;
&lt;br /&gt;
For development of Toaster we recommend setting up a local install of Toaster. General install instructions are available in the main [https://www.yoctoproject.org/documentation/toaster-manual Toaster documentation]&lt;br /&gt;
&lt;br /&gt;
Clone the poky repository&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ git clone git://git.yoctoproject.org/poky&lt;br /&gt;
    $ cd poky&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install a python virtual environment to sandbox the python modules from your OS.&lt;br /&gt;
Enter Activate the python virtual environment in your current shell.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ virtualenv venv&lt;br /&gt;
    $ source ./venv/bin/activate&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install the python module dependencies for Toaster&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ pip install -r ./bitbake/toaster-requirements.txt&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run the setup and start script, follow instructions displayed&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
     $ TOASTER_MANAGED=1 TOASTER_DEVEL=1 ./bitbake/bin/toaster&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Submitting patches ==&lt;br /&gt;
&lt;br /&gt;
Publishing your patches to Toaster is a two step process.&lt;br /&gt;
# Sending patches to Toaster Project for review&lt;br /&gt;
# Submitting the patches that you reviewed to the upstream repository&lt;br /&gt;
&lt;br /&gt;
Toaster code lives in Bitbake repository at [http://git.openembedded.org/bitbake/|http://git.openembedded.org/bitbake/].&lt;br /&gt;
All contributions must be upstreamed to the Bitbake repository in order to make it to the &amp;quot;master&amp;quot; branch of the poky/ repository.&lt;br /&gt;
&lt;br /&gt;
=== Sending patches to Toaster Project ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;NOTE:&amp;lt;/strong&amp;gt; The format of the commit message should be like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    toaster: &amp;lt;short one line summary&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    long(er) description&lt;br /&gt;
&lt;br /&gt;
    [YOCTO #0000]&lt;br /&gt;
&lt;br /&gt;
    Signed-off-by: First Last &amp;lt;name@domain.com&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where YOCTO #0000 is the related bug number if there is one. Signed off by with your git commit -s credentials.&lt;br /&gt;
&lt;br /&gt;
We accept patches on the [https://www.yoctoproject.org/tools-resources/community/mailing-lists toaster mailing list] by &amp;quot;git send-email&amp;quot; please include in your subject line &amp;quot;[review-request][PATCH]&amp;quot; &lt;br /&gt;
&lt;br /&gt;
e.g.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ git send-email HEAD^  --subject-prefix=&amp;quot;review-request][PATCH&amp;quot; &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A comprehensive document about commit messages is available on the [http://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines openembedded wiki]&lt;br /&gt;
&lt;br /&gt;
More help learning git is available on [https://try.github.io github] and [http://git-scm.com/documentation/ the official documentation]&lt;br /&gt;
&lt;br /&gt;
=== Sending branches to Toaster Project ===&lt;br /&gt;
&lt;br /&gt;
If you wish to submit whole branches please use the poky-contrib repository see [[Poky Contributions#Poky_Contrib_Branch]] for setup guide.&lt;br /&gt;
&lt;br /&gt;
Once you have pushed a branch please then send an email to the [https://www.yoctoproject.org/tools-resources/community/mailing-lists toaster mailing list] with the subject in the following format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 [review-request] my_branch_name&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the body of the email it&#039;s useful to describe your branch&#039;s functionality, which commits and a link to the git web.&lt;br /&gt;
&lt;br /&gt;
If you need any assistance please post on the mailing list.&lt;br /&gt;
&lt;br /&gt;
=== Submitting patch sets for integration into Bitbake ===&lt;br /&gt;
&lt;br /&gt;
All Toaster patches need to be submitted upstream to the Bitbake repository after they have been reviewed on the Toaster mailing list. Since development happens on the poky-contrib repository, but the patches need to be merged to the Bitbake repository, the following process should be executed.&lt;br /&gt;
&lt;br /&gt;
1) Fetch the target branch and checkout the target branch&lt;br /&gt;
   &amp;lt;code&amp;gt;git fetch contrib the/target/branch &amp;amp;&amp;amp; git checkout the/target/branch&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) Create a new branch for submission &lt;br /&gt;
   &amp;lt;code&amp;gt; git checkout -b yourname/submit/the/target/branch &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3) Make sure the branch is rebased on current poky master. &lt;br /&gt;
   &amp;lt;code&amp;gt;git pull --rebase origin master&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4) Add signed off by to the commit messages&lt;br /&gt;
   &amp;lt;code&amp;gt;git filter-branch -f --msg-filter &#039;cat &amp;amp;&amp;amp; echo &amp;quot;Signed-off-by: $(git config --get user.name) &amp;lt;$(git config --get user.email)&amp;gt;&amp;quot;&#039; master..HEAD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
5) Push the modified commit messages and rebased version to poky-contrib&lt;br /&gt;
   &amp;lt;code&amp;gt;git push -u contrib yourname/submit/the/target/branch &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
6) Use the create-pull-request script (from poky) to create a pull request&lt;br /&gt;
   &amp;lt;code&amp;gt;./scripts/create-pull-request -d bitbake -s &amp;quot;Fixes and clean ups&amp;quot; -u contrib&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
7) Review their content, especially the summary mail:&lt;br /&gt;
   &amp;lt;code&amp;gt;edit ./pull-&amp;lt;pid&amp;gt;/0000-cover-letter.patch&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are satisfied, you can send them with:&lt;br /&gt;
   &amp;lt;code&amp;gt;./scripts/send-pull-request -a -p ./pull-&amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to bitbake-devel@lists.openembedded.org&lt;br /&gt;
&lt;br /&gt;
==== Submitting patches for prior releases ====&lt;br /&gt;
&lt;br /&gt;
The procedure is the same, but using the prior release as the base branch instead of the &amp;quot;master&amp;quot; branch in bitbake.&lt;br /&gt;
&lt;br /&gt;
Also, make sure that you add the name of the prior release for which the patchset is intended in the prefix of the patchset, as parameter to the &amp;quot;create-pull-request&amp;quot; command, e.g. &#039;&#039;&#039;-p 1.26&#039;&#039;&#039; for the 1.26 branch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Gotchas ====&lt;br /&gt;
&lt;br /&gt;
Sometimes the mailer will refuse to send patches, especially on binary or long-line files. The proper way to go around that is to reply to the patchset you&#039;ve submitted to the mailing list, asking for a git pull directly from the poky-contrib branch.&lt;br /&gt;
&lt;br /&gt;
&#039;&lt;br /&gt;
&lt;br /&gt;
== Code syle guide ==&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
&lt;br /&gt;
Django has a template language which allows us to render pages based on the data (context). We use the template language to setup the initial state of the page and to create re-usable components that can be included in other pages.&lt;br /&gt;
&lt;br /&gt;
The recommend template code style is as follows&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Yes please:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{var}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
  {# Maintaining indentation #}&lt;br /&gt;
  {% if %}&lt;br /&gt;
   &amp;lt;p&amp;gt;this&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% else %}&lt;br /&gt;
   &amp;lt;p&amp;gt;that&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{% comment %}&lt;br /&gt;
This is a longer comment that describes all the things&lt;br /&gt;
that are below in quite a bit of detail because they&#039;re&lt;br /&gt;
a little more difficult to understand.&lt;br /&gt;
{% endcomment %}&lt;br /&gt;
&lt;br /&gt;
{% for layer in layers_list %}&lt;br /&gt;
 {{layer}}&lt;br /&gt;
{% endfor %}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;No thank you:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{var}}&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
{# Maintaining indentation #}&lt;br /&gt;
{%if%}&amp;lt;p&amp;gt;this&amp;lt;/p&amp;gt;{%else%}&amp;lt;p&amp;gt;that&amp;lt;/p&amp;gt;{%endif%}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{#This is a longer comment that describes all the things that are below in quite a bit of detail because they&#039;re a little more difficult to understand. #}&lt;br /&gt;
{%for o in layers_list%}{{o}}{%endfor%}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* Maintain indentation as you would with other languages&lt;br /&gt;
* White space after &#039;%&#039;&lt;br /&gt;
* Comment blocks for longer comments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Javascript ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Yes please:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/* These hold some numbers */&lt;br /&gt;
var oneVar = 1;&lt;br /&gt;
var twoVar = 2;&lt;br /&gt;
&lt;br /&gt;
var cheesesTypes = {&lt;br /&gt;
  cheddar : 1,&lt;br /&gt;
  stilton : 2,&lt;br /&gt;
  emmental : 3, &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
function doThingsHere(){&lt;br /&gt;
  return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* If one equals two do some other things and make sure that&lt;br /&gt;
 * if the the click handler is setup correctly.&lt;br /&gt;
 */&lt;br /&gt;
if (one === two) {&lt;br /&gt;
  var cheese = &amp;quot;cheddar&amp;quot;;&lt;br /&gt;
  oneVar = doThingsHere();&lt;br /&gt;
&lt;br /&gt;
  $(this).click(function (event){&lt;br /&gt;
    alert(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$(&amp;quot;#little-mouse&amp;quot;).focusout(function(){&lt;br /&gt;
  alert(&amp;quot;bye&amp;quot;)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if (oneVar)&lt;br /&gt;
  noThingHere();&lt;br /&gt;
else&lt;br /&gt;
  doThingHere();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;No thank you:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// These hold some numbers&lt;br /&gt;
oneVar = 1&lt;br /&gt;
twoVar = 2&lt;br /&gt;
&lt;br /&gt;
var cheesesTypes = { cheddar : 1, stilton : 2,  emmental : 3, }&lt;br /&gt;
&lt;br /&gt;
function doThingsHere ()&lt;br /&gt;
{&lt;br /&gt;
return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//If one equals two do some other things and make sure that if the the click handler is setup correctly.&lt;br /&gt;
if( one === two ) {&lt;br /&gt;
var cheese = &amp;quot;cheddar&amp;quot;;&lt;br /&gt;
oneVar = doThingsHere();&lt;br /&gt;
&lt;br /&gt;
    $(this).click(function(event){ alert(&amp;quot;Hello&amp;quot;); });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
document.getElementById(&amp;quot;little-mouse&amp;quot;).addEventListener(&amp;quot;focusout&amp;quot;, function(){&lt;br /&gt;
  alert(&amp;quot;bye&amp;quot;)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if (oneVar)&lt;br /&gt;
{&lt;br /&gt;
  noThingHere();&lt;br /&gt;
} else {  doThingHere(); }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* Variables should be marked with &amp;quot;var&amp;quot; &lt;br /&gt;
* Semicolons should be used&lt;br /&gt;
* Keep as close to 80 cols as possible&lt;br /&gt;
* Use 2 space per indentation&lt;br /&gt;
* Open curly braces after parenthesis for functions and close on a new line&lt;br /&gt;
* Use camelCase for function names and variable names &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make use of running your Javascript through jshint we have a .jshint configuration file in that js directory (toastergui/static/js)&lt;br /&gt;
&lt;br /&gt;
e.g. install jshint and add to your current PATH, then run:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ npm install jshint; export PATH=$PATH:$PWD/node_modules/.bin/&lt;br /&gt;
 $ jshint ./toastergui/static/js/base.js&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTML ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Yes please:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;something-area&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;p class=&amp;quot;important&amp;quot;&amp;gt;This is some text&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;important-text&amp;gt;This is some text&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;No thank you:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;somethingarea&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p class=&amp;quot;Important&amp;quot;&amp;gt;This is some text&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;somethingarea&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;ImportantText&amp;quot;&amp;gt;This is&lt;br /&gt;
some text&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* 2 space indentation&lt;br /&gt;
* Lower case, ids hyphenated when multiple words&lt;br /&gt;
* No duplicate ids &lt;br /&gt;
&lt;br /&gt;
* Run your HTML through a [http://validator.w3.org/#validate_by_input HTML validator] available for [http://validator.w3.org/source/ local install]. The w3c validator it&#039;s self doesn&#039;t currently validate html5, it uses as a back end [https://validator.github.io/validator/ Nu Html Checker] which can be installed as a standalone service, full instructions in the readme.&lt;br /&gt;
&lt;br /&gt;
Quick install instructions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $ mkdir html5-validator &amp;amp;&amp;amp; cd html5-validator&lt;br /&gt;
 $ export JAVA_HOME=/usr/lib/jvm/java-6-openjdk&lt;br /&gt;
 $ git clone https://github.com/validator/validator.git&lt;br /&gt;
 $ python build/build.py all&lt;br /&gt;
 $ python build/build.py all&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
HTML can be indented quickly using tidy, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 tidy -xml --indent auto --indent-spaces 2 --quiet yes -w -1 --show-body-only yes  ./index.html &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
Lenient [https://www.python.org/dev/peps/pep-0008 pep8]&lt;br /&gt;
Ignoring most of the whitespace around character issues (E124,E203,E201,E265,E303,E302,E231) see toaster/.pep8 and [http://pep8.readthedocs.org/en/latest/intro.html#error-codes error code list]&lt;br /&gt;
&lt;br /&gt;
Fix all issues identified by running code through pep8. We have a fairly lenient config file (toaster/.pep8).&lt;br /&gt;
&lt;br /&gt;
e.g.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ pep8 ./toastergui/urls.py&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run code through pylint and fix identified issues - Some can be reasonably ignored such as doc strings for every function or star-args. No pylintrc config provided here as most issues identified are highly contextual and should be ignored on a case by case basis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ pylint --load-plugins pylint_django toastergui/tests.py&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Working with design ==&lt;br /&gt;
&lt;br /&gt;
Yes, the Yocto Project is one of those lucky projects with designers around to help in UI matters. We have a document explaining how to work with the design contributors: [[File:Working_with_design.pdf]]&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Contribute_to_Toaster&amp;diff=15967</id>
		<title>Contribute to Toaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Contribute_to_Toaster&amp;diff=15967"/>
		<updated>2015-09-24T13:27:35Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* Sending patches to Toaster Project */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
This page summarises the Toaster development process. We hope this will help you start contributing to the project. &lt;br /&gt;
&lt;br /&gt;
== What can I do? ==&lt;br /&gt;
&lt;br /&gt;
The [https://bugzilla.yoctoproject.org/buglist.cgi?product=Toaster Yocto Project Bugzilla instance] lists all the things that need to be done:&lt;br /&gt;
&lt;br /&gt;
* If the issue says &amp;lt;strong&amp;gt;GUI design available&amp;lt;/strong&amp;gt; in the Whiteboard field, there is a design specification document attached to the issue that you should follow. Send questions / comments about it to the [https://lists.yoctoproject.org/listinfo/toaster Toaster mailing list]&lt;br /&gt;
* If the issue says &amp;lt;strong&amp;gt;GUI design pending&amp;lt;/strong&amp;gt; in the Whiteboard field, there is some design work still to be done. Feel free to take the issue and send an email to the [https://lists.yoctoproject.org/listinfo/toaster Toaster mailing list] to find out why the design work is not done yet&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Set up the local repository ==&lt;br /&gt;
&lt;br /&gt;
For development of Toaster we recommend setting up a local install of Toaster. General install instructions are available in the main [https://www.yoctoproject.org/documentation/toaster-manual Toaster documentation]&lt;br /&gt;
&lt;br /&gt;
Clone the poky repository&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ git clone git://git.yoctoproject.org/poky&lt;br /&gt;
    $ cd poky&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install a python virtual environment to sandbox the python modules from your OS.&lt;br /&gt;
Enter Activate the python virtual environment in your current shell.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ virtualenv venv&lt;br /&gt;
    $ source ./venv/bin/activate&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Install the python module dependencies for Toaster&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ pip install -r ./bitbake/toaster-requirements.txt&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run the setup and start script, follow instructions displayed&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
     $ TOASTER_MANAGED=1 TOASTER_DEVEL=1 ./bitbake/bin/toaster&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Submitting patches ==&lt;br /&gt;
&lt;br /&gt;
Publishing your patches to Toaster is a two step process.&lt;br /&gt;
# Sending patches to Toaster Project for review&lt;br /&gt;
# Submitting the patches that you reviewed to the upstream repository&lt;br /&gt;
&lt;br /&gt;
Toaster code lives in Bitbake repository at [http://git.openembedded.org/bitbake/|http://git.openembedded.org/bitbake/].&lt;br /&gt;
All contributions must be upstreamed to the Bitbake repository in order to make it to the &amp;quot;master&amp;quot; branch of the poky/ repository.&lt;br /&gt;
&lt;br /&gt;
=== Sending patches to Toaster Project ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;NOTE:&amp;lt;/strong&amp;gt; The format of the commit message should be like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    toaster: &amp;lt;short one line summary&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    long(er) description&lt;br /&gt;
&lt;br /&gt;
    [YOCTO #0000]&lt;br /&gt;
&lt;br /&gt;
    Signed-off-by: First Last &amp;lt;name@domain.com&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where YOCTO #0000 is the related bug number if there is one. Signed off by with your git commit -s credentials.&lt;br /&gt;
&lt;br /&gt;
We accept patches on the [https://www.yoctoproject.org/tools-resources/community/mailing-lists toaster mailing list] by &amp;quot;git send-email&amp;quot; please include in your subject line &amp;quot;[review-request][PATCH]&amp;quot; &lt;br /&gt;
&lt;br /&gt;
e.g.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
    $ git send-email HEAD^  --subject-prefix=&amp;quot;review-request][PATCH&amp;quot; &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A comprehensive document about commit messages is available on the [http://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines openembedded wiki]&lt;br /&gt;
&lt;br /&gt;
More help learning git is available on [https://try.github.io github] and [http://git-scm.com/documentation/ the official documentation]&lt;br /&gt;
&lt;br /&gt;
=== Sending branches to Toaster Project ===&lt;br /&gt;
&lt;br /&gt;
If you wish to submit whole branches please use the poky-contrib repository see [[Poky Contributions#Poky_Contrib_Branch]] for setup guide.&lt;br /&gt;
&lt;br /&gt;
Once you have pushed a branch please then send an email to the [https://www.yoctoproject.org/tools-resources/community/mailing-lists toaster mailing list] with the subject in the following format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 [review-request] my_branch_name&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the body of the email it&#039;s useful to describe your branch&#039;s functionality, which commits and a link to the git web.&lt;br /&gt;
&lt;br /&gt;
If you need any assistance please post on the mailing list.&lt;br /&gt;
&lt;br /&gt;
=== Submitting patch sets for integration into Bitbake ===&lt;br /&gt;
&lt;br /&gt;
All Toaster patches need to be submitted upstream to the Bitbake repository after they have been reviewed on the Toaster mailing list. Since development happens on the poky-contrib repository, but the patches need to be merged to the Bitbake repository, the following process should be executed.&lt;br /&gt;
&lt;br /&gt;
1) Fetch the target branch and checkout the target branch&lt;br /&gt;
   &amp;lt;code&amp;gt;git fetch contrib the/target/branch &amp;amp;&amp;amp; git checkout the/target/branch&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2) Create a new branch for submission &lt;br /&gt;
   &amp;lt;code&amp;gt; git checkout -b yourname/submit/the/target/branch &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3) Make sure the branch is rebased on current master. &lt;br /&gt;
   &amp;lt;code&amp;gt;git pull --rebase origin master&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4) Add signed off by to the commit messages&lt;br /&gt;
   &amp;lt;code&amp;gt;git filter-branch -f --msg-filter &#039;cat &amp;amp;&amp;amp; echo &amp;quot;Signed-off-by: $(git config --get user.name) &amp;lt;$(git config --get user.email)&amp;gt;&amp;quot;&#039; master..HEAD&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
5) Push the modified commit messages and rebased version to poky-contrib&lt;br /&gt;
   &amp;lt;code&amp;gt;git push -u contrib yourname/submit/the/target/branch &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
6) Use the create-pull-request script (from poky) to create a pull request&lt;br /&gt;
   &amp;lt;code&amp;gt;./scripts/create-pull-request -d bitbake -s &amp;quot;Fixes and clean ups&amp;quot; -u contrib&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
7) Review their content, especially the summary mail:&lt;br /&gt;
   &amp;lt;code&amp;gt;edit ./pull-&amp;lt;pid&amp;gt;/0000-cover-letter.patch&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are satisfied, you can send them with:&lt;br /&gt;
   &amp;lt;code&amp;gt;./scripts/send-pull-request -a -p ./pull-&amp;lt;pid&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to bitbake-devel@lists.openembedded.org&lt;br /&gt;
&lt;br /&gt;
==== Submitting patches for prior releases ====&lt;br /&gt;
&lt;br /&gt;
The procedure is the same, but using the prior release as the base branch instead of the &amp;quot;master&amp;quot; branch in bitbake.&lt;br /&gt;
&lt;br /&gt;
Also, make sure that you add the name of the prior release for which the patchset is intended in the prefix of the patchset, as parameter to the &amp;quot;create-pull-request&amp;quot; command, e.g. &#039;&#039;&#039;-p 1.26&#039;&#039;&#039; for the 1.26 branch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Gotchas ====&lt;br /&gt;
&lt;br /&gt;
Sometimes the mailer will refuse to send patches, especially on binary or long-line files. The proper way to go around that is to reply to the patchset you&#039;ve submitted to the mailing list, asking for a git pull directly from the poky-contrib branch.&lt;br /&gt;
&lt;br /&gt;
&#039;&lt;br /&gt;
&lt;br /&gt;
== Code syle guide ==&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
&lt;br /&gt;
Django has a template language which allows us to render pages based on the data (context). We use the template language to setup the initial state of the page and to create re-usable components that can be included in other pages.&lt;br /&gt;
&lt;br /&gt;
The recommend template code style is as follows&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Yes please:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{var}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
  {# Maintaining indentation #}&lt;br /&gt;
  {% if %}&lt;br /&gt;
   &amp;lt;p&amp;gt;this&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% else %}&lt;br /&gt;
   &amp;lt;p&amp;gt;that&amp;lt;/p&amp;gt;&lt;br /&gt;
  {% endif %}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{% comment %}&lt;br /&gt;
This is a longer comment that describes all the things&lt;br /&gt;
that are below in quite a bit of detail because they&#039;re&lt;br /&gt;
a little more difficult to understand.&lt;br /&gt;
{% endcomment %}&lt;br /&gt;
&lt;br /&gt;
{% for layer in layers_list %}&lt;br /&gt;
 {{layer}}&lt;br /&gt;
{% endfor %}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;No thank you:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{var}}&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
{# Maintaining indentation #}&lt;br /&gt;
{%if%}&amp;lt;p&amp;gt;this&amp;lt;/p&amp;gt;{%else%}&amp;lt;p&amp;gt;that&amp;lt;/p&amp;gt;{%endif%}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{#This is a longer comment that describes all the things that are below in quite a bit of detail because they&#039;re a little more difficult to understand. #}&lt;br /&gt;
{%for o in layers_list%}{{o}}{%endfor%}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* Maintain indentation as you would with other languages&lt;br /&gt;
* White space after &#039;%&#039;&lt;br /&gt;
* Comment blocks for longer comments&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Javascript ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Yes please:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;use strict&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
/* These hold some numbers */&lt;br /&gt;
var oneVar = 1;&lt;br /&gt;
var twoVar = 2;&lt;br /&gt;
&lt;br /&gt;
var cheesesTypes = {&lt;br /&gt;
  cheddar : 1,&lt;br /&gt;
  stilton : 2,&lt;br /&gt;
  emmental : 3, &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
function doThingsHere(){&lt;br /&gt;
  return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* If one equals two do some other things and make sure that&lt;br /&gt;
 * if the the click handler is setup correctly.&lt;br /&gt;
 */&lt;br /&gt;
if (one === two) {&lt;br /&gt;
  var cheese = &amp;quot;cheddar&amp;quot;;&lt;br /&gt;
  oneVar = doThingsHere();&lt;br /&gt;
&lt;br /&gt;
  $(this).click(function (event){&lt;br /&gt;
    alert(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$(&amp;quot;#little-mouse&amp;quot;).focusout(function(){&lt;br /&gt;
  alert(&amp;quot;bye&amp;quot;)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if (oneVar)&lt;br /&gt;
  noThingHere();&lt;br /&gt;
else&lt;br /&gt;
  doThingHere();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;No thank you:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// These hold some numbers&lt;br /&gt;
oneVar = 1&lt;br /&gt;
twoVar = 2&lt;br /&gt;
&lt;br /&gt;
var cheesesTypes = { cheddar : 1, stilton : 2,  emmental : 3, }&lt;br /&gt;
&lt;br /&gt;
function doThingsHere ()&lt;br /&gt;
{&lt;br /&gt;
return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//If one equals two do some other things and make sure that if the the click handler is setup correctly.&lt;br /&gt;
if( one === two ) {&lt;br /&gt;
var cheese = &amp;quot;cheddar&amp;quot;;&lt;br /&gt;
oneVar = doThingsHere();&lt;br /&gt;
&lt;br /&gt;
    $(this).click(function(event){ alert(&amp;quot;Hello&amp;quot;); });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
document.getElementById(&amp;quot;little-mouse&amp;quot;).addEventListener(&amp;quot;focusout&amp;quot;, function(){&lt;br /&gt;
  alert(&amp;quot;bye&amp;quot;)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
if (oneVar)&lt;br /&gt;
{&lt;br /&gt;
  noThingHere();&lt;br /&gt;
} else {  doThingHere(); }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* Variables should be marked with &amp;quot;var&amp;quot; &lt;br /&gt;
* Semicolons should be used&lt;br /&gt;
* Keep as close to 80 cols as possible&lt;br /&gt;
* Use 2 space per indentation&lt;br /&gt;
* Open curly braces after parenthesis for functions and close on a new line&lt;br /&gt;
* Use camelCase for function names and variable names &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make use of running your Javascript through jshint we have a .jshint configuration file in that js directory (toastergui/static/js)&lt;br /&gt;
&lt;br /&gt;
e.g. install jshint and add to your current PATH, then run:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ npm install jshint; export PATH=$PATH:$PWD/node_modules/.bin/&lt;br /&gt;
 $ jshint ./toastergui/static/js/base.js&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTML ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Yes please:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;something-area&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;p class=&amp;quot;important&amp;quot;&amp;gt;This is some text&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
  &amp;lt;p id=&amp;quot;important-text&amp;gt;This is some text&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;No thank you:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;somethingarea&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p class=&amp;quot;Important&amp;quot;&amp;gt;This is some text&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;somethingarea&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;p id=&amp;quot;ImportantText&amp;quot;&amp;gt;This is&lt;br /&gt;
some text&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* 2 space indentation&lt;br /&gt;
* Lower case, ids hyphenated when multiple words&lt;br /&gt;
* No duplicate ids &lt;br /&gt;
&lt;br /&gt;
* Run your HTML through a [http://validator.w3.org/#validate_by_input HTML validator] available for [http://validator.w3.org/source/ local install]. The w3c validator it&#039;s self doesn&#039;t currently validate html5, it uses as a back end [https://validator.github.io/validator/ Nu Html Checker] which can be installed as a standalone service, full instructions in the readme.&lt;br /&gt;
&lt;br /&gt;
Quick install instructions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $ mkdir html5-validator &amp;amp;&amp;amp; cd html5-validator&lt;br /&gt;
 $ export JAVA_HOME=/usr/lib/jvm/java-6-openjdk&lt;br /&gt;
 $ git clone https://github.com/validator/validator.git&lt;br /&gt;
 $ python build/build.py all&lt;br /&gt;
 $ python build/build.py all&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
HTML can be indented quickly using tidy, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 tidy -xml --indent auto --indent-spaces 2 --quiet yes -w -1 --show-body-only yes  ./index.html &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Python ===&lt;br /&gt;
&lt;br /&gt;
Lenient [https://www.python.org/dev/peps/pep-0008 pep8]&lt;br /&gt;
Ignoring most of the whitespace around character issues (E124,E203,E201,E265,E303,E302,E231) see toaster/.pep8 and [http://pep8.readthedocs.org/en/latest/intro.html#error-codes error code list]&lt;br /&gt;
&lt;br /&gt;
Fix all issues identified by running code through pep8. We have a fairly lenient config file (toaster/.pep8).&lt;br /&gt;
&lt;br /&gt;
e.g.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ pep8 ./toastergui/urls.py&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run code through pylint and fix identified issues - Some can be reasonably ignored such as doc strings for every function or star-args. No pylintrc config provided here as most issues identified are highly contextual and should be ignored on a case by case basis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 $ pylint --load-plugins pylint_django toastergui/tests.py&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Working with design ==&lt;br /&gt;
&lt;br /&gt;
Yes, the Yocto Project is one of those lucky projects with designers around to help in UI matters. We have a document explaining how to work with the design contributors: [[File:Working_with_design.pdf]]&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=15767</id>
		<title>Toaster and bitbake communications</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=15767"/>
		<updated>2015-09-11T14:53:16Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* main.py (bitbake/lib/bb/main.py) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a write up of some investigation I did into how toaster asks bitbake to do stuff, and how it hears about what bitbake is doing. It&#039;s not definitive and not guaranteed to be correct, but it might provide some pointers if you are working on toaster. If anyone knows better, please feel free to correct this.&lt;br /&gt;
&lt;br /&gt;
Any paths given below are relative to the root of the poky/poky-contrib source tree.&lt;br /&gt;
&lt;br /&gt;
== toaster asking bitbake to do stuff ==&lt;br /&gt;
&lt;br /&gt;
First off I wanted to figure out how clicking on the &amp;quot;Build&amp;quot; button in the toaster interface triggers a bitbake build.&lt;br /&gt;
&lt;br /&gt;
toaster only asks bitbake to perform builds when in managed mode. This is the point I started from when doing this investigation. I think analysis mode is very similar, except toaster doesn&#039;t ask bitbake to do anything, it just listens to stuff which is already happening.&lt;br /&gt;
&lt;br /&gt;
=== runbuilds ===&lt;br /&gt;
&lt;br /&gt;
The conversation with bitbake is handled via the runbuilds command (bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py). This command is run in a loop once every second from the toaster start script (bitbake/bin/toaster).&lt;br /&gt;
&lt;br /&gt;
The runbuilds.schedule() function looks for BuildRequests in the database. A BuildRequest is created each time you press a &amp;quot;Build&amp;quot; button in the toaster web interface. Those build requests are then used to invoke the &amp;quot;real&amp;quot; builds in bitbake.&lt;br /&gt;
&lt;br /&gt;
* schedule() gets a localhostbecontroller (be = build environment) instance (there is a becontroller for remote bitbakes, but I don&#039;t think the implementation is complete) and assigns it to a variable bec.&lt;br /&gt;
&lt;br /&gt;
* schedule() then calls bec.triggerBuild() for a build request which is in the BuildRequest.REQ_QUEUED state. Only one build request gets picked up each time the runbuilds script runs: the one with the largest ID.&lt;br /&gt;
&lt;br /&gt;
* schedule() also creates a build identification variable for each build request, combining the primary key of the build request with the primary key of the build environment controller&#039;s build environment (!)).&lt;br /&gt;
&lt;br /&gt;
=== localhostbecontroller ===&lt;br /&gt;
&lt;br /&gt;
This is the build environment controller, which handles instances of build environments.&lt;br /&gt;
&lt;br /&gt;
It is a subclass of BuildEnvironmentController.&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() calls getBBController(), which returns a BitBakeController instance (bbctrl). getBBController() actually instantiates the controller if it isn&#039;t already instantiated, passing it a &amp;quot;server&amp;quot; object. This server object is an instance of bb.server.xmlrpc.BitBakeXMLRPCClient().&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() then calls the BitBakeController build() method: bbctrl.build()&lt;br /&gt;
&lt;br /&gt;
=== BitBakeController (bbctrl) ===&lt;br /&gt;
&lt;br /&gt;
This is constructed with the server object, which is an XMLRPC connection to a BitBake server.&lt;br /&gt;
&lt;br /&gt;
* build() invokes the &amp;quot;buildTargets&amp;quot; command on the connection using runCommand().&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCClient (bitbake/lib/bb/server/xmlrpc.py) ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
It has a &amp;quot;connection&amp;quot; member, which has runCommand() invoked on it.&lt;br /&gt;
&lt;br /&gt;
When constructed by getBBController(), initServer() is called as part of its construction. This is a method from BitBakeServer which sets the interface address (default: (localhost,0)) and creates an XMLRPCServer() using this interface address.&lt;br /&gt;
&lt;br /&gt;
Once initServer() is done, establishConnection() is invoked, which creates the &amp;quot;real&amp;quot; socket.&lt;br /&gt;
&lt;br /&gt;
The established connection (which runCommand() is invoked on) is a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeServer ===&lt;br /&gt;
&lt;br /&gt;
BitbakeServer is a subclass of BitBakeBaseServer (declared in bitbake/lib/bb/server/__init__.py); this actually has the addcooker() method which associates a bitbake cooker instance with the connection.&lt;br /&gt;
&lt;br /&gt;
BitBakeBaseServer acts like a decorator around a server implementation; in our case, it&#039;s a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
Calling addcooker() on the BitBakeBaseServer also calls it on any server implementation (serverImpl) which is wrapped by BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCServerConnection ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeBaseServerConnection.&lt;br /&gt;
&lt;br /&gt;
When runCommand() is invoked on this, it&#039;s passed on to the cooker object associated with it, i.e. it actually invokes self.cooker.command.runCommand().&lt;br /&gt;
&lt;br /&gt;
The cooker object is associated with the BitBakeXMLRPCServerConnection when the bitbake/lib/bb/main.py script runs.&lt;br /&gt;
&lt;br /&gt;
=== main.py ===&lt;br /&gt;
&lt;br /&gt;
The main script which starts a bitbake server and ui. This is what runs when you use &amp;quot;bitbake&amp;quot; from the command line.&lt;br /&gt;
&lt;br /&gt;
toaster starts the bitbake server with the --server-only switch, which calls bitbake_main() in this file; this in turn calls the main() function.&lt;br /&gt;
&lt;br /&gt;
This instantiates a bb.cooker.BBCooker and adds it to the server implementation via addcooker(). The cooker is what actually enables commands to be sent to the bitbake server.&lt;br /&gt;
&lt;br /&gt;
=== BBCooker (bitbake/lib/bb/cooker.py) ===&lt;br /&gt;
&lt;br /&gt;
This has a &amp;quot;command&amp;quot; property which is an instance of BBCommand; this is what runCommand() finally gets invoked on.&lt;br /&gt;
&lt;br /&gt;
=== BBCommand ===&lt;br /&gt;
&lt;br /&gt;
runCommand() calls a method from an instance of CommandSync (all the synchronous commands bitbake understands) or CommandAsync (the asynchronous ones), depending on the type of command passed to runCommand(). Both the CommandSync and CommandAsync instances are added to the BBCooker when it is created.&lt;br /&gt;
&lt;br /&gt;
For example, runCommand(&amp;quot;getVariable&amp;quot;, ...) is invoked via CommandsSync.getVariable().&lt;br /&gt;
&lt;br /&gt;
The commands which go through BBCommand.runCommand() now make their way to the bitbake server over XMLRPC.&lt;br /&gt;
&lt;br /&gt;
== toaster listening to what bitbake is doing ==&lt;br /&gt;
&lt;br /&gt;
At this point, I realised I probably understood enough to see how bitbake was being invoked from toaster: asking toaster to start a build sends a &amp;quot;buildTargets&amp;quot; command to the bitbake XMLRPC server, via a rather indirect series of objects and method calls.&lt;br /&gt;
&lt;br /&gt;
What I wanted to know now was how toaster listens to the result of that command. At a high level, I understood that it gathered events from the XMLRPC connection and converted them into database objects. However, I didn&#039;t really get the code path.&lt;br /&gt;
&lt;br /&gt;
I started from BuildInfoHelper, which is where bitbake events are converted into toaster db objects.&lt;br /&gt;
&lt;br /&gt;
=== BuildInfoHelper ===&lt;br /&gt;
&lt;br /&gt;
This is passed build events in the toasterui.py script.&lt;br /&gt;
&lt;br /&gt;
BuildInfoHelper is responsible for constructing toaster ORM objects from events. The BuildInfoHelper is constructed with a server (instance of BitBakeXMLRPCServerConnection in the case of toaster), so it can also interrogate the bitbake server for extra environmental data via getVariable().&lt;br /&gt;
&lt;br /&gt;
=== toasterui.py ===&lt;br /&gt;
&lt;br /&gt;
This has a main() function which sets up an event listener loop.&lt;br /&gt;
&lt;br /&gt;
main() is passed a server, eventHandler and params. The eventHandler is the object which listens to bitbake events.&lt;br /&gt;
&lt;br /&gt;
toasterui is called from main.py (see below).&lt;br /&gt;
&lt;br /&gt;
=== main.py (bitbake/lib/bb/main.py) ===&lt;br /&gt;
&lt;br /&gt;
The toasterui.py main() method is invoked from main.py.&lt;br /&gt;
&lt;br /&gt;
It&#039;s passed two parameters:&lt;br /&gt;
&lt;br /&gt;
# server_connection.connection&lt;br /&gt;
# server_connection.events&lt;br /&gt;
&lt;br /&gt;
The second of these is the eventHandler which is set up in a loop in toasterui.py.&lt;br /&gt;
&lt;br /&gt;
server_connection comes from a call to establishConnection() in main.py. The server is constructed via a servermodule, which is dynamically chosen in main.py according to the parameters used to invoke it (-t). It will either be &amp;quot;process&amp;quot; or &amp;quot;xmlrpc&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The UI type is set in the main.py script via the -u option.&lt;br /&gt;
&lt;br /&gt;
toaster invokes bitbake with: -t xmlrpc -u toasterui&lt;br /&gt;
&lt;br /&gt;
which means that we get the toasterui.main() called, and we get an xmlrpc bitbake server.&lt;br /&gt;
&lt;br /&gt;
Back to server_connection.events...&lt;br /&gt;
&lt;br /&gt;
This refers to an xmlrpc bitbake server connection&#039;s events object.&lt;br /&gt;
&lt;br /&gt;
So toasterui.main() gets:&lt;br /&gt;
&lt;br /&gt;
# server_connection.connection =&amp;gt; BitBakeXMLRPCServerConnection.connection&lt;br /&gt;
# server_connection.events =&amp;gt; BitBakeXMLRPCServerConnection.events&lt;br /&gt;
&lt;br /&gt;
The events object is an instance of uievent.BBUIEventQueue in our case, as we&#039;re using toasterui.&lt;br /&gt;
&lt;br /&gt;
=== uievent.BBUIEventQueue (bitbake/lib/bb/ui/uievent.py) ===&lt;br /&gt;
&lt;br /&gt;
This is our event handler (&amp;quot;events&amp;quot;) in toasterui.main(), which is receiving events on the XMLRPC connection (see later).&lt;br /&gt;
&lt;br /&gt;
toasterui.main() calls events.waitEvent(0.25), which looks for events on the queue every 0.25s. If the queue has events, one is popped off.&lt;br /&gt;
&lt;br /&gt;
Each event popped from the queue is passed to uihelper.BBUIHelper.eventHandler(), which adds build tracking information (how many packages built, tasks completed etc.)&lt;br /&gt;
&lt;br /&gt;
The event then goes to the BuildInfoHelper, where it gets stored in toaster&#039;s database.&lt;br /&gt;
&lt;br /&gt;
==== How do events get on BBUIEventQueue? ====&lt;br /&gt;
&lt;br /&gt;
BBUIEventQueue is instantiated on the BitBakeXMLRPCServerConnection object.&lt;br /&gt;
&lt;br /&gt;
It is passed a BBServer, which is an xmlrpclib.ServerProxy (from the Python standard library).&lt;br /&gt;
&lt;br /&gt;
The ServerProxy has a method corresponding to each method on the server it is proxying for; in this case, the proxied server is a BBServer, so the proxy has the same methods as BBServer.&lt;br /&gt;
&lt;br /&gt;
Event handling is set up by calling self.BBServer.registerEventHandler() from BBUIEventQueue.&lt;br /&gt;
&lt;br /&gt;
registerEventHandler() is defined in bitbake/lib/bb/server/xmlrpc.py, BitBakeServerCommands. This in turn calls bb.event.register_UIHandler, defined in bitbake/lib/bb/event.py.&lt;br /&gt;
&lt;br /&gt;
When an event is fired, each handler registered for it is invoked with that event (in event.py).&lt;br /&gt;
&lt;br /&gt;
The main function for firing events in event.py is fire_from_worker(), which is called from bitbake/lib/bb/runqueue.py.&lt;br /&gt;
&lt;br /&gt;
Events are constructed from xmlrpc messages coming from the bitbake server (see runqueue.py, runQueuePipe.read()).&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
At this point, I felt I understood enough about how events are processed for my purpose, so I didn&#039;t dig any further. I knew where I could amend an event to add/remove properties on it, which is what I was after.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=15766</id>
		<title>Toaster and bitbake communications</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster_and_bitbake_communications&amp;diff=15766"/>
		<updated>2015-09-11T14:51:14Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: Created page with &amp;quot;This is a write up of some investigation I did into how toaster asks bitbake to do stuff, and how it hears about what bitbake is doing. It&amp;#039;s not definitive and not guaranteed ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a write up of some investigation I did into how toaster asks bitbake to do stuff, and how it hears about what bitbake is doing. It&#039;s not definitive and not guaranteed to be correct, but it might provide some pointers if you are working on toaster. If anyone knows better, please feel free to correct this.&lt;br /&gt;
&lt;br /&gt;
Any paths given below are relative to the root of the poky/poky-contrib source tree.&lt;br /&gt;
&lt;br /&gt;
== toaster asking bitbake to do stuff ==&lt;br /&gt;
&lt;br /&gt;
First off I wanted to figure out how clicking on the &amp;quot;Build&amp;quot; button in the toaster interface triggers a bitbake build.&lt;br /&gt;
&lt;br /&gt;
toaster only asks bitbake to perform builds when in managed mode. This is the point I started from when doing this investigation. I think analysis mode is very similar, except toaster doesn&#039;t ask bitbake to do anything, it just listens to stuff which is already happening.&lt;br /&gt;
&lt;br /&gt;
=== runbuilds ===&lt;br /&gt;
&lt;br /&gt;
The conversation with bitbake is handled via the runbuilds command (bitbake/lib/toaster/bldcontrol/management/commands/runbuilds.py). This command is run in a loop once every second from the toaster start script (bitbake/bin/toaster).&lt;br /&gt;
&lt;br /&gt;
The runbuilds.schedule() function looks for BuildRequests in the database. A BuildRequest is created each time you press a &amp;quot;Build&amp;quot; button in the toaster web interface. Those build requests are then used to invoke the &amp;quot;real&amp;quot; builds in bitbake.&lt;br /&gt;
&lt;br /&gt;
* schedule() gets a localhostbecontroller (be = build environment) instance (there is a becontroller for remote bitbakes, but I don&#039;t think the implementation is complete) and assigns it to a variable bec.&lt;br /&gt;
&lt;br /&gt;
* schedule() then calls bec.triggerBuild() for a build request which is in the BuildRequest.REQ_QUEUED state. Only one build request gets picked up each time the runbuilds script runs: the one with the largest ID.&lt;br /&gt;
&lt;br /&gt;
* schedule() also creates a build identification variable for each build request, combining the primary key of the build request with the primary key of the build environment controller&#039;s build environment (!)).&lt;br /&gt;
&lt;br /&gt;
=== localhostbecontroller ===&lt;br /&gt;
&lt;br /&gt;
This is the build environment controller, which handles instances of build environments.&lt;br /&gt;
&lt;br /&gt;
It is a subclass of BuildEnvironmentController.&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() calls getBBController(), which returns a BitBakeController instance (bbctrl). getBBController() actually instantiates the controller if it isn&#039;t already instantiated, passing it a &amp;quot;server&amp;quot; object. This server object is an instance of bb.server.xmlrpc.BitBakeXMLRPCClient().&lt;br /&gt;
&lt;br /&gt;
* triggerBuild() then calls the BitBakeController build() method: bbctrl.build()&lt;br /&gt;
&lt;br /&gt;
=== BitBakeController (bbctrl) ===&lt;br /&gt;
&lt;br /&gt;
This is constructed with the server object, which is an XMLRPC connection to a BitBake server.&lt;br /&gt;
&lt;br /&gt;
* build() invokes the &amp;quot;buildTargets&amp;quot; command on the connection using runCommand().&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCClient (bitbake/lib/bb/server/xmlrpc.py) ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
It has a &amp;quot;connection&amp;quot; member, which has runCommand() invoked on it.&lt;br /&gt;
&lt;br /&gt;
When constructed by getBBController(), initServer() is called as part of its construction. This is a method from BitBakeServer which sets the interface address (default: (localhost,0)) and creates an XMLRPCServer() using this interface address.&lt;br /&gt;
&lt;br /&gt;
Once initServer() is done, establishConnection() is invoked, which creates the &amp;quot;real&amp;quot; socket.&lt;br /&gt;
&lt;br /&gt;
The established connection (which runCommand() is invoked on) is a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeServer ===&lt;br /&gt;
&lt;br /&gt;
BitbakeServer is a subclass of BitBakeBaseServer (declared in bitbake/lib/bb/server/__init__.py); this actually has the addcooker() method which associates a bitbake cooker instance with the connection.&lt;br /&gt;
&lt;br /&gt;
BitBakeBaseServer acts like a decorator around a server implementation; in our case, it&#039;s a BitBakeXMLRPCServerConnection.&lt;br /&gt;
&lt;br /&gt;
Calling addcooker() on the BitBakeBaseServer also calls it on any server implementation (serverImpl) which is wrapped by BitBakeServer.&lt;br /&gt;
&lt;br /&gt;
=== BitBakeXMLRPCServerConnection ===&lt;br /&gt;
&lt;br /&gt;
This is a subclass of BitBakeBaseServerConnection.&lt;br /&gt;
&lt;br /&gt;
When runCommand() is invoked on this, it&#039;s passed on to the cooker object associated with it, i.e. it actually invokes self.cooker.command.runCommand().&lt;br /&gt;
&lt;br /&gt;
The cooker object is associated with the BitBakeXMLRPCServerConnection when the bitbake/lib/bb/main.py script runs.&lt;br /&gt;
&lt;br /&gt;
=== main.py ===&lt;br /&gt;
&lt;br /&gt;
The main script which starts a bitbake server and ui. This is what runs when you use &amp;quot;bitbake&amp;quot; from the command line.&lt;br /&gt;
&lt;br /&gt;
toaster starts the bitbake server with the --server-only switch, which calls bitbake_main() in this file; this in turn calls the main() function.&lt;br /&gt;
&lt;br /&gt;
This instantiates a bb.cooker.BBCooker and adds it to the server implementation via addcooker(). The cooker is what actually enables commands to be sent to the bitbake server.&lt;br /&gt;
&lt;br /&gt;
=== BBCooker (bitbake/lib/bb/cooker.py) ===&lt;br /&gt;
&lt;br /&gt;
This has a &amp;quot;command&amp;quot; property which is an instance of BBCommand; this is what runCommand() finally gets invoked on.&lt;br /&gt;
&lt;br /&gt;
=== BBCommand ===&lt;br /&gt;
&lt;br /&gt;
runCommand() calls a method from an instance of CommandSync (all the synchronous commands bitbake understands) or CommandAsync (the asynchronous ones), depending on the type of command passed to runCommand(). Both the CommandSync and CommandAsync instances are added to the BBCooker when it is created.&lt;br /&gt;
&lt;br /&gt;
For example, runCommand(&amp;quot;getVariable&amp;quot;, ...) is invoked via CommandsSync.getVariable().&lt;br /&gt;
&lt;br /&gt;
The commands which go through BBCommand.runCommand() now make their way to the bitbake server over XMLRPC.&lt;br /&gt;
&lt;br /&gt;
== toaster listening to what bitbake is doing ==&lt;br /&gt;
&lt;br /&gt;
At this point, I realised I probably understood enough to see how bitbake was being invoked from toaster: asking toaster to start a build sends a &amp;quot;buildTargets&amp;quot; command to the bitbake XMLRPC server, via a rather indirect series of objects and method calls.&lt;br /&gt;
&lt;br /&gt;
What I wanted to know now was how toaster listens to the result of that command. At a high level, I understood that it gathered events from the XMLRPC connection and converted them into database objects. However, I didn&#039;t really get the code path.&lt;br /&gt;
&lt;br /&gt;
I started from BuildInfoHelper, which is where bitbake events are converted into toaster db objects.&lt;br /&gt;
&lt;br /&gt;
=== BuildInfoHelper ===&lt;br /&gt;
&lt;br /&gt;
This is passed build events in the toasterui.py script.&lt;br /&gt;
&lt;br /&gt;
BuildInfoHelper is responsible for constructing toaster ORM objects from events. The BuildInfoHelper is constructed with a server (instance of BitBakeXMLRPCServerConnection in the case of toaster), so it can also interrogate the bitbake server for extra environmental data via getVariable().&lt;br /&gt;
&lt;br /&gt;
=== toasterui.py ===&lt;br /&gt;
&lt;br /&gt;
This has a main() function which sets up an event listener loop.&lt;br /&gt;
&lt;br /&gt;
main() is passed a server, eventHandler and params. The eventHandler is the object which listens to bitbake events.&lt;br /&gt;
&lt;br /&gt;
toasterui is called from main.py (see below).&lt;br /&gt;
&lt;br /&gt;
=== main.py (bitbake/lib/bb/main.py) ===&lt;br /&gt;
&lt;br /&gt;
The toasterui.py main() method is invoked from main.py.&lt;br /&gt;
&lt;br /&gt;
It&#039;s passed two parameters:&lt;br /&gt;
&lt;br /&gt;
1. server_connection.connection&lt;br /&gt;
2. server_connection.events&lt;br /&gt;
&lt;br /&gt;
The second of these is the eventHandler which is set up in a loop in toasterui.py.&lt;br /&gt;
&lt;br /&gt;
server_connection comes from a call to establishConnection() in main.py. The server is constructed via a servermodule, which is dynamically chosen in main.py according to the parameters used to invoke it (-t). It will either be &amp;quot;process&amp;quot; or &amp;quot;xmlrpc&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The UI type is set in the main.py script via the -u option.&lt;br /&gt;
&lt;br /&gt;
toaster invokes bitbake with: -t xmlrpc -u toasterui&lt;br /&gt;
&lt;br /&gt;
which means that we get the toasterui.main() called, and we get an xmlrpc bitbake server.&lt;br /&gt;
&lt;br /&gt;
Back to server_connection.events...&lt;br /&gt;
&lt;br /&gt;
This refers to an xmlrpc bitbake server connection&#039;s events object.&lt;br /&gt;
&lt;br /&gt;
So toasterui.main() gets:&lt;br /&gt;
&lt;br /&gt;
1. server_connection.connection =&amp;gt; BitBakeXMLRPCServerConnection.connection&lt;br /&gt;
2. server_connection.events =&amp;gt; BitBakeXMLRPCServerConnection.events&lt;br /&gt;
&lt;br /&gt;
The events object is an instance of uievent.BBUIEventQueue in our case, as we&#039;re using toasterui.&lt;br /&gt;
&lt;br /&gt;
=== uievent.BBUIEventQueue (bitbake/lib/bb/ui/uievent.py) ===&lt;br /&gt;
&lt;br /&gt;
This is our event handler (&amp;quot;events&amp;quot;) in toasterui.main(), which is receiving events on the XMLRPC connection (see later).&lt;br /&gt;
&lt;br /&gt;
toasterui.main() calls events.waitEvent(0.25), which looks for events on the queue every 0.25s. If the queue has events, one is popped off.&lt;br /&gt;
&lt;br /&gt;
Each event popped from the queue is passed to uihelper.BBUIHelper.eventHandler(), which adds build tracking information (how many packages built, tasks completed etc.)&lt;br /&gt;
&lt;br /&gt;
The event then goes to the BuildInfoHelper, where it gets stored in toaster&#039;s database.&lt;br /&gt;
&lt;br /&gt;
==== How do events get on BBUIEventQueue? ====&lt;br /&gt;
&lt;br /&gt;
BBUIEventQueue is instantiated on the BitBakeXMLRPCServerConnection object.&lt;br /&gt;
&lt;br /&gt;
It is passed a BBServer, which is an xmlrpclib.ServerProxy (from the Python standard library).&lt;br /&gt;
&lt;br /&gt;
The ServerProxy has a method corresponding to each method on the server it is proxying for; in this case, the proxied server is a BBServer, so the proxy has the same methods as BBServer.&lt;br /&gt;
&lt;br /&gt;
Event handling is set up by calling self.BBServer.registerEventHandler() from BBUIEventQueue.&lt;br /&gt;
&lt;br /&gt;
registerEventHandler() is defined in bitbake/lib/bb/server/xmlrpc.py, BitBakeServerCommands. This in turn calls bb.event.register_UIHandler, defined in bitbake/lib/bb/event.py.&lt;br /&gt;
&lt;br /&gt;
When an event is fired, each handler registered for it is invoked with that event (in event.py).&lt;br /&gt;
&lt;br /&gt;
The main function for firing events in event.py is fire_from_worker(), which is called from bitbake/lib/bb/runqueue.py.&lt;br /&gt;
&lt;br /&gt;
Events are constructed from xmlrpc messages coming from the bitbake server (see runqueue.py, runQueuePipe.read()).&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
&lt;br /&gt;
At this point, I felt I understood enough about how events are processed for my purpose, so I didn&#039;t dig any further. I knew where I could amend an event to add/remove properties on it, which is what I was after.&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
	<entry>
		<id>https://wiki.yoctoproject.org/wiki/index.php?title=Toaster&amp;diff=15765</id>
		<title>Toaster</title>
		<link rel="alternate" type="text/html" href="https://wiki.yoctoproject.org/wiki/index.php?title=Toaster&amp;diff=15765"/>
		<updated>2015-09-11T14:47:55Z</updated>

		<summary type="html">&lt;p&gt;Elliot Smith: /* About Toaster */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Toaster]]&lt;br /&gt;
[[Toaster]] is a web-based interface to OpenEmbedded and BitBake. You might have heard it is a replacement for [https://www.yoctoproject.org/documentation/hob-manual Hob]. The answer is &amp;quot;it will be at some point, but not yet&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
General discussion about &#039;&#039;&#039;Toaster&#039;&#039;&#039; happens on a dedicated mailing list: [https://lists.yoctoproject.org/listinfo/toaster https://lists.yoctoproject.org/listinfo/toaster]&lt;br /&gt;
&lt;br /&gt;
=== Using Toaster ===&lt;br /&gt;
&lt;br /&gt;
Toaster can run in various modes and setups. To ease out the understanding of documentation, we review here the terminology used throughout the documentation.&lt;br /&gt;
&lt;br /&gt;
* Interactive mode - this is the mode released with Yocto Project Release 1.6. Functional components consist of a build recording component, &#039;toasterui&#039;, and a build stats and inspection user interface, the &#039;toastergui&#039;. It is started with the command sequence listed below, and the builds are started using normal bitbake command line&lt;br /&gt;
&lt;br /&gt;
 $ source oe-init-build-env&lt;br /&gt;
 $ source toaster start&lt;br /&gt;
&lt;br /&gt;
* Managed  mode - in this mode Toaster handles the build configuration GUI (through Project pages), and build scheduling and execution, in addition to the features launched with Yocto Project Release 1.6. The builds are triggered through the web interface, &lt;br /&gt;
the user as no direct access to the bitbake command line.&lt;br /&gt;
&lt;br /&gt;
** Local managed mode, in short _local_, is the out-of-box mode available after a poky/ checkout. It allows the user to perform build on his local machine source code, with a local build directory, and is intended to help the user discover the interface, and configure and run local builds. You can launch it with&lt;br /&gt;
&lt;br /&gt;
 $ ./bitbake/bin/toaster&lt;br /&gt;
&lt;br /&gt;
** Remote managed mode, or [[hosted Toaster]], is intended to be the production setup for running Toaster in organizations supporting multiple users and using customized Toaster installations.&lt;br /&gt;
&lt;br /&gt;
=== Toaster How-to&#039;s ===&lt;br /&gt;
&lt;br /&gt;
Specific pages with Toaster how-tos are available below.&lt;br /&gt;
&lt;br /&gt;
* [[Contribute to Toaster]]&lt;br /&gt;
* [[Testing Toaster]]&lt;br /&gt;
* [[Setting up a local instance of Toaster]]&lt;br /&gt;
* [[Setting up a production instance of Toaster]] - documentation for Interactive mode&lt;br /&gt;
* [[Setting up a hosted managed mode for Toaster]] - configure and run build through Toaster itself, providing a service for your users&lt;br /&gt;
* [https://www.yoctoproject.org/documentation/toaster-manual-17 How to use the Toaster web interface]&lt;br /&gt;
* [[How to delete information from the Toaster database]]&lt;br /&gt;
&lt;br /&gt;
=== About Toaster ===&lt;br /&gt;
&lt;br /&gt;
* [[File:Working_with_design.pdf]]&lt;br /&gt;
* [https://bugzilla.yoctoproject.org/buglist.cgi?list_id=213820&amp;amp;columnlist=status_whiteboard%2Cassigned_to%2Ctarget_milestone%2Cbug_status%2Cshort_desc%2Cbug_severity%2Cpriority&amp;amp;classification=Build%20System%20%26%20Metadata&amp;amp;query_based_on=Toaster-Opens&amp;amp;query_format=advanced&amp;amp;bug_status=NEW&amp;amp;bug_status=ACCEPTED&amp;amp;bug_status=IN%20PROGRESS%20DESIGN&amp;amp;bug_status=IN%20PROGRESS%20DESIGN%20COMPLETE&amp;amp;bug_status=IN%20PROGRESS%20IMPLEMENTATION&amp;amp;bug_status=REOPENED&amp;amp;bug_status=NEEDINFO&amp;amp;bug_status=WaitForUpstream&amp;amp;component=toaster&amp;amp;product=Toaster&amp;amp;known_name=Toaster-Opens Bug list]&lt;br /&gt;
* [[Toaster architecture design]]&lt;br /&gt;
* [[Toaster and bitbake communications]]&lt;br /&gt;
* [[Toaster testing plan]]&lt;br /&gt;
* [[Toaster 1.9 design | Scope and design (1.9)]]&lt;br /&gt;
* [[Toaster 1.7 design | Scope and design (1.7 - 1.8)]]&lt;br /&gt;
* [[Toaster archive | Archive]]&lt;br /&gt;
&lt;br /&gt;
=== In progress documentation ===&lt;br /&gt;
&lt;br /&gt;
We are currently preparing the documentation for the Toaster build functionality. The content here is just a brain dump of what we need to cover (in no particular order). Feel free to add and create content as you see fit:&lt;br /&gt;
&lt;br /&gt;
*[[Using virtualenv]]&lt;br /&gt;
*[[Toaster configuration]] - explain releases, layer sources, BitBake versions and default project configurations &lt;br /&gt;
*[[Set up Toaster locally]] - explain the set up process and the local version of the localconf.json file&lt;br /&gt;
*[[Set up production instance]] - explain the Django admin interface and the remove version of the localconf.json file&lt;br /&gt;
*[[manage.py commands]] - this should include an explanation of lsupdates&lt;br /&gt;
*[[Custom layer index]] - explain how to create your own layer index&lt;br /&gt;
*[[Start Toaster in managed mode]]&lt;/div&gt;</summary>
		<author><name>Elliot Smith</name></author>
	</entry>
</feed>