Custom Search

Wednesday, December 18, 2013

OpenStack Horizon Development Tutorial Part 1

1) Form
#######

1)
Main Form classes

a)
Location forms.py
horizon/horizon/forms/base.py

b)
Custom First Base class-1
class SelfHandlingMixin(object):

* Please check the methods of this class
* Every subclass of this class should implement the method "def handle(self, request, data):".
* You can see a statement like "handled = form.handle(self.request, form.cleaned_data)"
in the method "def form_valid(self, form):" of base view class "ModalFormView". That means
every horizon views inherited from "ModalFormView" should invoke the method "handle" of form class.
* Note: Horizon making API request from the method "handle" of Form class.

c)
Custom First Base class-2
class SelfHandlingForm(SelfHandlingMixin, forms.Form):

d)
Custom First Base class-3
class BaseUserForm(forms.SelfHandlingForm):


2) View
#######


1)
Main View Classes
a)
Location of views.py
horizon/horizon/forms/views.py

b)
Custom Base class-1
class ModalFormMixin(object):


c)
Custom Base class-2
generic.FormView


*A view that displays a form. On error, redisplays the form with validation errors; on success, redirects to a new URL.

https://docs.djangoproject.com/en/dev/ref/class-based-views/flattened-index/#formview
https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-editing/
https://docs.djangoproject.com/en/dev/ref/class-based-views/generic-editing/#formview
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-editing/#processformview

d)
Custom Base class-3
class ModalFormView(ModalFormMixin, generic.FormView):


'ModalFormView' is the main view class from which all views which handle forms in Horizon
should inherit. It takes care of all details with processing
:class:`~horizon.forms.base.SelfHandlingForm` classes, and modal concerns
when the associated template inherits from `horizon/common/_modal_form.html`.

Subclasses must define a ``form_class`` and ``template_name`` attribute at minimum.

See Django's documentation on the `FormView /en/dev/ref/class-based-views/generic-editing/#formview>`_ class for more details.

2)

Important methods of View Class

a)
def get_context_data(self, **kwargs):

* Returns a dictionary representing the template context. The keyword arguments provided will make up the returned context.
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-simple/#django.views.generic.base.ContextMixin.get_context_data

b)
def form_valid(self, form):

* Redirects to get_success_url()
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.form_valid

c)
get_form()

* Instantiate an instance of form_class using get_form_kwargs().
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.get_form

d)
get_form_kwargs()

* Build the keyword arguments required to instantiate the form.
* The initial argument is set to get_initial().
* If the request is a POST or PUT, the request data (request.POST and request.FILES) will also be provided.
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.get_form_kwargs

e)
get_initial()
* Retrieve initial data for the form. By default, returns a copy of initial.
https://docs.djangoproject.com/en/dev/ref/class-based-views/flattened-index/#formview
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-editing/#django.views.generic.edit.FormMixin.get_initial

f)
def get_object(self):

* Returns the single object that this view will display. If queryset is provided, that queryset will be used as the source of objects; otherwise, get_queryset() will be used. get_object() looks for a pk_url_kwarg argument in the arguments to the view; if this argument is found, this method performs a primary-key based lookup using that value. If this argument is not found, it looks for a slug_url_kwarg argument, and performs a slug lookup using the slug_field.

https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_object

g)
You can see following methods in all horizon view classs
def get_success_url(self):
def get_context_data(self, **kwargs):
def get_initial(self):
def get_form_kwargs(self):



3) Templates
##########


1)
a)

Main model form template
./horizon/templates/horizon/common/_modal_form.html

* This is a common template to display form tag.
* This template "_modal_form.html" included in all other templates in the
horizon as base template.

* You can see following include statement here
{% include "horizon/common/_form_fields.html" %}

b)
./horizon/templates/horizon/common/_form_fields.html
* This template "_form_fields.html" included in all other templates in the
horizon to show fields. So this is a common template to display form fields.
* You can see "errors", "hidden fields" and "visible_fields" loop here.

2)

Another important base template is "horizon/templates/base.html"
There you can see following include statements
{% include "_stylesheets.html" %}
{% include "horizon/_conf.html" %}
{% include "horizon/client_side/_script_loader.html" %}
{% include 'horizon/common/_sidebar.html' %}
{% include "_header.html" %}
{% include "horizon/_messages.html" %}
{% include "horizon/_scripts.html" %}


4) JavaScript
###########


1)
* Please where most of the js files are included.
* Please where "$(document).ready(" is calling and initializing horizon js object.

Location
-------
./horizon/templates/horizon/_scripts.html

initialization part
-------------------
* In "_scripts.html" you can see an include statement {% include "horizon/client_side/templates.html" %}

2)
* This "_scripts.html" is included in "horizon/templates/base.html"
* This "horizon/templates/base.html" is included in lot of create, update, add and change htmls.

3)
Main javascript file
location
--------------
./horizon/static/horizon/js/horizon.js

In this file we can see following things

a)
Definition of "Horizon" object.

b)
Definition of initialization method "horizon.init"

c)
// Create the one and only horizon object.
var horizon = new Horizon();

d)
Please check the following important methods of "Horizon" object.

* "horizon.addInitFunction"
Use the addInitFunction() function to add initialization code which must
be called on DOM ready. This is useful for adding things like event
handlers or any other initialization functions which should preceed user
interaction but rely on DOM readiness.You can see the use of this method
in all other javascript files.

* "horizon.init"
Call all initialization functions and clear the queue


5) Exceptions
############


a)
Files Location
./horizon/exceptions.py
./openstack_dashboard/exceptions.py

b)
Main exception handle method (./horizon/exceptions.py)

def handle(request, message=None, redirect=None, ignore=False,
           escalate=False, log_level=None, force_log=None):
    """Centralized error handling for Horizon.

    Because Horizon consumes so many different APIs with completely
    different ``Exception`` types, it's necessary to have a centralized
    place for handling exceptions which may be raised.

    Exceptions are roughly divided into 3 types:

    #. ``UNAUTHORIZED``: Errors resulting from authentication or authorization
       problems. These result in being logged out and sent to the login screen.
    #. ``NOT_FOUND``: Errors resulting from objects which could not be
       located via the API. These generally result in a user-facing error
       message, but are otherwise returned to the normal code flow. Optionally
       a redirect value may be passed to the error handler so users are
       returned to a different view than the one requested in addition to the
       error message.
    #. RECOVERABLE: Generic API errors which generate a user-facing message
       but drop directly back to the regular code flow.

    All other exceptions bubble the stack as normal unless the ``ignore``
    argument is passed in as ``True``, in which case only unrecognized
    errors are bubbled.

    If the exception is not re-raised, an appropriate wrapper exception
    class indicating the type of exception that was encountered will be
    returned.
    """
c)
Value of constant exceptions.UNAUTHORIZED (./horizon/exceptions.py)
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
-------------------------------------------------
(class 'keystoneclient.apiclient.exceptions.Unauthorized'>, class 'keystoneclient.apiclient.exceptions.Forbidden'>, class 'cinderclient.exceptions.Unauthorized'>, class 'cinderclient.exceptions.Forbidden'>, class 'novaclient.exceptions.Unauthorized'>, class 'novaclient.exceptions.Forbidden'>, class 'glanceclient.exc.Unauthorized'>, class 'neutronclient.common.exceptions.Unauthorized'>, class 'neutronclient.common.exceptions.Forbidden'>, class 'heatclient.exc.HTTPUnauthorized'>, class 'heatclient.exc.HTTPForbidden'>, class 'troveclient.exceptions.Unauthorized'>)


Value of constant exceptions.NOT_FOUND (./horizon/exceptions.py)
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
-------------------------------------------------
(class 'keystoneclient.apiclient.exceptions.NotFound'>, class 'cinderclient.exceptions.NotFound'>, class 'novaclient.exceptions.NotFound'>, class 'glanceclient.exc.NotFound'>, class 'neutronclient.common.exceptions.NetworkNotFoundClient'>, class 'neutronclient.common.exceptions.PortNotFoundClient'>, class 'heatclient.exc.HTTPNotFound'>, class 'troveclient.exceptions.NotFound'>)


Value of constant exceptions.RECOVERABLE (./horizon/exceptions.py)
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
-------------------------------------------------
(class 'horizon.exceptions.AlreadyExists'>, class 'keystoneclient.apiclient.exceptions.ClientException'>, class 'keystoneclient.apiclient.exceptions.AuthorizationFailure'>, class 'cinderclient.exceptions.ClientException'>, class 'cinderclient.exceptions.ConnectionError'>, class 'novaclient.exceptions.ClientException'>, class 'glanceclient.exc.ClientException'>, class 'neutronclient.common.exceptions.NeutronClientException'>, class 'neutronclient.common.exceptions.NetworkInUseClient'>, class 'neutronclient.common.exceptions.PortInUseClient'>, class 'neutronclient.common.exceptions.AlreadyAttachedClient'>, class 'neutronclient.common.exceptions.StateInvalidClient'>, class 'swiftclient.exceptions.ClientException'>, class 'heatclient.exc.HTTPException'>, class 'troveclient.exceptions.ClientException'>)

d)
How to add new exceptions to the list UNAUTHORIZED, NOT_FOUND and RECOVERABLE
Goto folowing file asnd add there
./openstack_dashboard/exceptions.py

No comments:

Post a Comment