============================================ Tutorial: Adding a complex action to a table ============================================ This tutorial covers how to add a more complex action to a table, one that requires an action and form definitions, as well as changes to the view, urls, and table. This tutorial assumes you have already completed :doc:`Building a Dashboard using Horizon `. If not, please do so now as we will be modifying the files created there. This action will create a snapshot of the instance. When the action is taken, it will display a form that will allow the user to enter a snapshot name, and will create that snapshot when the form is closed using the ``Create snapshot`` button. Defining the view ================= To define the view, we must create a view class, along with the template (``HTML``) file and the form class for that view. The template file ----------------- The template file contains the HTML that will be used to show the view. Create a ``create_snapshot.html`` file under the ``mypanel/templates/mypanel`` directory and add the following code:: {% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Snapshot" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Create a Snapshot") %} {% endblock page_header %} {% block main %} {% include 'mydashboard/mypanel/_create_snapshot.html' %} {% endblock %} As you can see, the main body will be defined in ``_create_snapshot.html``, so we must also create that file under the ``mypanel/templates/mypanel`` directory. It should contain the following code:: {% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}
{% trans "Snapshots preserve the disk state of a running instance." %}
{% endblock %} The form -------- Horizon provides a :class:`~horizon.forms.base.SelfHandlingForm` class which simplifies some of the details involved in creating a form. Our form will derive from this class, adding a character field to allow the user to specify a name for the snapshot, and handling the successful closure of the form by calling the nova api to create the snapshot. Create the ``forms.py`` file under the ``mypanel`` directory and add the following:: from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from openstack_dashboard import api class CreateSnapshot(forms.SelfHandlingForm): instance_id = forms.CharField(label=_("Instance ID"), widget=forms.HiddenInput(), required=False) name = forms.CharField(max_length=255, label=_("Snapshot Name")) def handle(self, request, data): try: snapshot = api.nova.snapshot_create(request, data['instance_id'], data['name']) return snapshot except Exception: exceptions.handle(request, _('Unable to create snapshot.')) The view -------- Now, the view will tie together the template and the form. Horizon provides a :class:`~horizon.forms.views.ModalFormView` class which simplifies the creation of a view that will contain a modal form. Open the ``views.py`` file under the ``mypanel`` directory and add the code for the CreateSnapshotView and the necessary imports. The complete file should now look something like this:: from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import tabs from horizon import exceptions from horizon import forms from horizon.utils import memoized from openstack_dashboard import api from openstack_dashboard.dashboards.mydashboard.mypanel \ import forms as project_forms from openstack_dashboard.dashboards.mydashboard.mypanel \ import tabs as mydashboard_tabs class IndexView(tabs.TabbedTableView): tab_group_class = mydashboard_tabs.MypanelTabs # A very simple class-based view... template_name = 'mydashboard/mypanel/index.html' def get_data(self, request, context, *args, **kwargs): # Add data to the context here... return context class CreateSnapshotView(forms.ModalFormView): form_class = project_forms.CreateSnapshot template_name = 'mydashboard/mypanel/create_snapshot.html' success_url = reverse_lazy("horizon:project:images:index") modal_id = "create_snapshot_modal" modal_header = _("Create Snapshot") submit_label = _("Create Snapshot") submit_url = "horizon:mydashboard:mypanel:create_snapshot" @memoized.memoized_method def get_object(self): try: return api.nova.server_get(self.request, self.kwargs["instance_id"]) except Exception: exceptions.handle(self.request, _("Unable to retrieve instance.")) def get_initial(self): return {"instance_id": self.kwargs["instance_id"]} def get_context_data(self, **kwargs): context = super(CreateSnapshotView, self).get_context_data(**kwargs) instance_id = self.kwargs['instance_id'] context['instance_id'] = instance_id context['instance'] = self.get_object() context['submit_url'] = reverse(self.submit_url, args=[instance_id]) return context Adding the url ============== We must add the url for our new view. Open the ``urls.py`` file under the ``mypanel`` directory and add the following as a new url pattern:: url(r'^(?P