425 lines
14 KiB
ReStructuredText
425 lines
14 KiB
ReStructuredText
OpenERP Web Core and standard addons
|
|
====================================
|
|
|
|
* General organization and core ideas (design philosophies)
|
|
* Internal documentation, autodoc, Python and JS domains
|
|
* QWeb code documentation/description
|
|
* Documentation of the OpenERP APIs and choices taken based on that?
|
|
* Style guide and coding conventions (PEP8? More)
|
|
* Test frameworks in JS?
|
|
|
|
Standard Views
|
|
--------------
|
|
|
|
Search View
|
|
+++++++++++
|
|
|
|
The OpenERP search view really is a sub-view, used in support of views
|
|
acting on collections of records (list view or graph view, for
|
|
instance).
|
|
|
|
Its main goal is to collect information from its widgets (themselves
|
|
collecting information from the users) and make those available to the
|
|
rest of the client.
|
|
|
|
The search view's root is :js:class:`~openerp.base.SearchView`. This
|
|
object should never need to be created or managed directly, its
|
|
lifecycle should be driven by the
|
|
:js:class:`~openerp.base.ViewManager`.
|
|
|
|
.. TODO: insert SearchView constructor here
|
|
|
|
The search view defines a number of internal and external protocols to
|
|
communicate with the objects around and within it. Most of these
|
|
protocols are informal, and types available for inheritance are more
|
|
mixins than mandatory.
|
|
|
|
Events
|
|
""""""
|
|
|
|
``on_loaded``
|
|
|
|
.. TODO: method openerp.base.SearchView.on_loaded
|
|
|
|
Fires when the search view receives its view data (the result of
|
|
``fields_view_get``). Hooking up before the event allows for
|
|
altering view data before it can be used.
|
|
|
|
By the time ``on_loaded`` is done, the search view is guaranteed to
|
|
be fully set up and ready to use.
|
|
|
|
``on_search``
|
|
|
|
.. TODO: method openerp.base.SearchView.on_search
|
|
|
|
Event triggered after a user asked for a search. The search view
|
|
fires this event after collecting all input data (contexts, domains
|
|
and group_by contexts). Note that the search view does *not* merge
|
|
those (or otherwise evaluate them), they are returned as provided by
|
|
the various inputs within the view.
|
|
|
|
``on_clear``
|
|
|
|
.. TODO: method openerp.base.SearchView.on_clear
|
|
|
|
Triggered after a user asked for a form clearing.
|
|
|
|
Input management
|
|
""""""""""""""""
|
|
|
|
An important concept in the search view is that of input. It is both
|
|
an informal protocol and an abstract type that can be inherited from.
|
|
|
|
Inputs are widgets which can contain user data (a char widget for
|
|
instance, or a selection box). They are capable of action and of
|
|
reaction:
|
|
|
|
.. _views-search-registration:
|
|
|
|
``registration``
|
|
|
|
This is an input action. Inputs have to register themselves to the
|
|
main view (which they receive as a constructor argument). This is
|
|
performed by pushing themselves on the
|
|
:js:attr:`openerp.base.SearchView.inputs` array.
|
|
|
|
``get_context``
|
|
|
|
An input reaction. When it needs to collect contexts, the view calls
|
|
``get_context()`` on all its inputs.
|
|
|
|
Inputs can react in the following manners:
|
|
|
|
* Return a context (an object), this is the "normal" response if the
|
|
input holds a value.
|
|
|
|
* Return a value that evaluates as false (generally ``null``). This
|
|
value indicates the input does not contain any value and will not
|
|
affect the results of the search.
|
|
|
|
* Raise :js:class:`openerp.base.search.Invalid` to indicate that it
|
|
holds a value but this value can not be used in the search
|
|
(because it is incorrectly formatted or nonsensical). Raising
|
|
:js:class:`~openerp.base.search.Invalid` is guaranteed to cancel
|
|
the search process.
|
|
|
|
:js:class:`~openerp.base.search.Invalid` takes three mandatory
|
|
arguments: an identifier (a name for instance), the invalid value,
|
|
and a validation message indicating the issue.
|
|
|
|
``get_domain``
|
|
|
|
The second input reaction, the possible behaviors of inputs are the
|
|
same as for ``get_context``.
|
|
|
|
The :js:class:`openerp.base.search.Input` type implements registration
|
|
on its own, but its implementations of ``get_context`` and
|
|
``get_domain`` simply raise errors and *must* be overridden.
|
|
|
|
One last action is for filters, as an activation order has to be kept
|
|
on them for some controls (to establish the correct grouping sequence,
|
|
for instance).
|
|
|
|
To that end, filters can call
|
|
:js:func:`openerp.base.Search.do_toggle_filter`, providing themselves
|
|
as first argument.
|
|
|
|
Filters calling :js:func:`~openerp.base.Search.do_toggle_filter` also
|
|
need to implement a method called
|
|
:js:func:`~openerp.base.search.Filter.is_enabled`, which the search
|
|
view will use to know the current status of the filter.
|
|
|
|
The search view automatically triggers a search after calls to
|
|
:js:func:`~openerp.base.Search.do_toggle_filter`.
|
|
|
|
Life cycle
|
|
""""""""""
|
|
|
|
The search view has a pretty simple and linear life cycle, in three main steps:
|
|
|
|
:js:class:`~openerp.base.SearchView.init`
|
|
|
|
Nothing interesting happens here
|
|
|
|
:js:func:`~openerp.base.SearchView.start`
|
|
|
|
Called by the main view's creator, this is the main initialization
|
|
step for the list view.
|
|
|
|
It begins with a remote call to fetch the view's descriptors
|
|
(``fields_view_get``).
|
|
|
|
Once the remote call is complete, the ``on_loaded`` even happens,
|
|
holding three main operations:
|
|
|
|
:js:func:`~openerp.base.SearchView.make_widgets`
|
|
|
|
Builds and returns the top-level widgets of the search
|
|
view. Because it returns an array of widget lines (a 2-dimensional
|
|
matrix of widgets) it should be called recursively by container
|
|
widgets (:js:class:`openerp.base.search.Group` for instance).
|
|
|
|
:js:func:`~openerp.base.search.Widget.render`
|
|
|
|
Called by the search view on all top-level widgets. Container
|
|
widgets should recursively call this method on their own children
|
|
widgets.
|
|
|
|
Widgets are provided with a mapping of ``{name: value}`` holding
|
|
default values for the search view. They can freely pick their
|
|
initial values from there, but must pass the mapping to their
|
|
children widgets if they have any.
|
|
|
|
:js:func:`~openerp.base.search.Widget.start`
|
|
|
|
The last operation of the search view startup is to initialize all
|
|
its widgets in order. This is again done recursively (the search
|
|
view starts its children, which have to start their own children).
|
|
|
|
:js:func:`~openerp.base.SearchView.stop`
|
|
|
|
Used before discarding a search view, allows the search view to
|
|
disable its events and pass the message to its own widgets,
|
|
gracefully shutting down the whole view.
|
|
|
|
Widgets
|
|
"""""""
|
|
|
|
In a search view, the widget is simply a unit of display.
|
|
|
|
All widgets must be able to react to three events, which will be
|
|
called in this order:
|
|
|
|
:js:func:`~openerp.base.search.Widget.render`
|
|
|
|
Called with a map of default values. The widget must return a
|
|
``String``, which is its HTML representation. That string can be
|
|
empty (if the widget should not be represented).
|
|
|
|
Widgets are responsible for asking their children for rendering, and
|
|
for passing along the default values.
|
|
|
|
:js:func:`~openerp.base.search.Widget.start`
|
|
|
|
Called without arguments. At this point, the widget has been fully
|
|
rendered and can set its events up, if any.
|
|
|
|
The widget is responsible for starting its children, if it has any.
|
|
|
|
:js:func:`~openerp.base.search.Widget.stop`
|
|
|
|
Gives the widget the opportunity to unbind its events, remove itself
|
|
from the DOM and perform any other cleanup task it may have.
|
|
|
|
Even if the widget does not do anything itself, it is responsible
|
|
for shutting down its children.
|
|
|
|
An abstract type is available and can be inherited from, to simplify
|
|
the implementation of those tasks:
|
|
|
|
.. TODO: insert Widget here
|
|
|
|
.. remember to document all methods
|
|
|
|
Inputs
|
|
""""""
|
|
|
|
The search namespace (``openerp.base.search``) provides two more
|
|
abstract types, used to implement input widgets:
|
|
|
|
* :js:class:`openerp.base.search.Input` is the most basic input type,
|
|
it only implements :ref:`input registration
|
|
<views-search-registration>`.
|
|
|
|
If inherited from, descendant classes should not call its
|
|
implementations of :js:func:`~openerp.base.search.Input.get_context`
|
|
and :js:func:`~openerp.base.search.Input.get_domain`.
|
|
|
|
* :js:class:`openerp.base.search.Field` is used to implement more
|
|
"field" widgets (which allow the user to input potentially complex
|
|
values).
|
|
|
|
It provides various services for its subclasses:
|
|
|
|
* Sets up the field attributes, using attributes from the field and
|
|
the view node.
|
|
|
|
* It fills the widget with :js:class:`~openerp.base.search.Filter`
|
|
if the field has any child filter.
|
|
|
|
* It automatically generates an identifier based on the field type
|
|
and the field name, using
|
|
:js:func:`~openerp.base.search.Widget.make_id`.
|
|
|
|
* It sets up a basic (overridable)
|
|
:js:attr:`~openerp.base.search.Field.template` attribute, combined
|
|
with the previous tasks, this makes subclasses of
|
|
:js:class:`~openerp.base.search.Field` render themselves "for
|
|
free".
|
|
|
|
* It provides basic implementations of ``get_context`` and
|
|
``get_domain``, both hinging on the subclasses implementing
|
|
``get_value()`` (which should return a correct, converted
|
|
Javascript value):
|
|
|
|
:js:func:`~openerp.base.search.Field.get_context`
|
|
|
|
Checks if the field has a non-``null`` and non-empty
|
|
(``String``) value, and that the field has a ``context`` attr.
|
|
|
|
If both conditions are fullfilled, returns the context.
|
|
|
|
:js:func:`~openerp.base.search.Field.get_domain`
|
|
|
|
Only requires that the field has a non-``null`` and non-empty
|
|
value.
|
|
|
|
If the field has a ``filter_domain``, returns it
|
|
immediately. Otherwise, builds a context using the field's
|
|
name, the field :js:attr:`~openerp.base.search.Field.operator`
|
|
and the field value, and returns it.
|
|
|
|
.. TODO: insert Input, Field, Filter, and just about every Field subclass
|
|
|
|
List View
|
|
+++++++++
|
|
|
|
OpenERP Web's list views don't actually exist as such in OpenERP itself: a
|
|
list view is an OpenERP tree view in the ``view_mode`` form.
|
|
|
|
The overall purpose of a list view is to display collections of objects in two
|
|
main forms: per-object, where each object is a row in and of itself, and
|
|
grouped, where multiple objects are represented with a single row providing
|
|
an aggregated view of all grouped objects.
|
|
|
|
These two forms can be mixed within a single list view, if needed.
|
|
|
|
The root of a list view is :js:class:`openerp.base.ListView`, which may need
|
|
to be overridden (partially or fully) to control list behavior in non-view
|
|
cases (when using a list view as sub-component of a form widget for instance).
|
|
|
|
Creation and Initialization
|
|
"""""""""""""""""""""""""""
|
|
|
|
As with most OpenERP Web views, the list view's
|
|
:js:func:`~openerp.base.ListView.init` takes quite a number of arguments.
|
|
|
|
While most of them are the standard view constructor arguments
|
|
(``view_manager``, ``session``, ``element_id``, ``dataset`` and an
|
|
optional ``view_id``), the list view adds a number of options for basic
|
|
customization (without having to override methods or templates):
|
|
|
|
``selectable`` (default: ``true``)
|
|
Indicates that the list view should allow records to be selected
|
|
individually. Displays selection check boxes to the left of all record rows,
|
|
and allows for the triggering of the
|
|
:ref:`selection event <listview-events-selection>`.
|
|
``deletable`` (default: ``true``)
|
|
Indicates that the list view should allow records to be removed
|
|
individually. Displays a deletion button to the right of all record rows,
|
|
and allows for the triggering of the
|
|
:ref:`deletion event <listview-events-deletion>`.
|
|
``header`` (default: ``true``)
|
|
Indicates that list columns should bear a header sporting their name (for
|
|
non-action columns).
|
|
``addable`` (default: ``"New"``)
|
|
Indicates that a record addition/creation button should be displayed in
|
|
the list's header, along with its label. Also allows for the triggering of
|
|
the :ref:`record addition event <listview-events-addition>`.
|
|
``sortable`` (default: ``true``)
|
|
Indicates that the list view can be sorted per-column (by clicking on its
|
|
column headers).
|
|
|
|
.. TODO: event?
|
|
``reorderable`` (default: ``true``)
|
|
Indicates that the list view records can be reordered (and re-sequenced)
|
|
by drag and drop.
|
|
|
|
.. TODO: event?
|
|
|
|
Events
|
|
""""""
|
|
.. _listview-events-addition:
|
|
|
|
Addition
|
|
''''''''
|
|
The addition event is used to add a record to an existing list view. The
|
|
default behavior is to switch to the form view, on a new record.
|
|
|
|
Addition behavior can be overridden by replacing the
|
|
:js:func:`~openerp.base.ListView.do_add_record` method.
|
|
|
|
.. _listview-events-selection:
|
|
|
|
Selection
|
|
'''''''''
|
|
The selection event is triggered when a given record is selected in the list
|
|
view.
|
|
|
|
It can be overridden by replacing the
|
|
:js:func:`~openerp.base.ListView.do_select` method.
|
|
|
|
The default behavior is simply to hide or display the list-wise deletion button
|
|
depending on whether there are selected records or not.
|
|
|
|
.. _listview-events-deletion:
|
|
|
|
Deletion
|
|
''''''''
|
|
The deletion event is triggered when the user tries to remove 1..n records from
|
|
the list view, either individually or globally (via the header button).
|
|
|
|
Deletion can be overridden by replacing the
|
|
:js:func:`~openerp.base.ListView.do_delete` method. By default, this method
|
|
calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
|
|
entirely.
|
|
|
|
.. note::
|
|
|
|
the list-wise deletion button (next to the record addition button)
|
|
simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
|
|
obtaining all selected record ids, but it is possible to override it
|
|
alone by replacing
|
|
:js:func:`~openerp.base.ListView.do_delete_selected`.
|
|
|
|
Internal API Doc
|
|
----------------
|
|
|
|
Python
|
|
++++++
|
|
|
|
These classes should be moved to other sections of the doc as needed,
|
|
probably.
|
|
|
|
.. automodule:: web.common.http
|
|
:members:
|
|
:undoc-members:
|
|
|
|
See also: :class:`~web.common.session.OpenERPSession`,
|
|
:class:`~web.common.openerplib.main.OpenERPModel`
|
|
|
|
.. automodule:: web.controllers.main
|
|
:members:
|
|
:undoc-members:
|
|
|
|
Testing
|
|
-------
|
|
|
|
Python
|
|
++++++
|
|
|
|
Testing for the OpenERP Web core is similar to :ref:`testing addons
|
|
<addons-testing>`: the tests live in ``openerpweb.tests``, unittest2_
|
|
is the testing framework and tests can be run via either unittest2
|
|
(``unit2 discover``) or via nose_ (``nosetests``).
|
|
|
|
Tests for the OpenERP Web core can also be run using ``setup.py
|
|
test``.
|
|
|
|
|
|
.. _unittest2:
|
|
http://www.voidspace.org.uk/python/articles/unittest2.shtml
|
|
|
|
.. _nose:
|
|
http://somethingaboutorange.com/mrl/projects/nose/1.0.0/
|