[fix] problem in binary fields
[odoo/odoo.git] / doc / development.rst
1 OpenERP Web Core and standard addons
2 ====================================
3
4 * General organization and core ideas (design philosophies)
5 * Internal documentation, autodoc, Python and JS domains
6 * QWeb code documentation/description
7 * Documentation of the OpenERP APIs and choices taken based on that?
8 * Style guide and coding conventions (PEP8? More)
9 * Test frameworks in JS?
10
11 Standard Views
12 --------------
13
14 Search View
15 +++++++++++
16
17 The OpenERP search view really is a sub-view, used in support of views
18 acting on collections of records (list view or graph view, for
19 instance).
20
21 Its main goal is to collect information from its widgets (themselves
22 collecting information from the users) and make those available to the
23 rest of the client.
24
25 The search view's root is :js:class:`~openerp.base.SearchView`. This
26 object should never need to be created or managed directly, its
27 lifecycle should be driven by the
28 :js:class:`~openerp.base.ViewManager`.
29
30 .. TODO: insert SearchView constructor here
31
32 The search view defines a number of internal and external protocols to
33 communicate with the objects around and within it. Most of these
34 protocols are informal, and types available for inheritance are more
35 mixins than mandatory.
36
37 Events
38 """"""
39
40 ``on_loaded``
41
42   .. TODO: method openerp.base.SearchView.on_loaded
43
44   Fires when the search view receives its view data (the result of
45   ``fields_view_get``). Hooking up before the event allows for
46   altering view data before it can be used.
47
48   By the time ``on_loaded`` is done, the search view is guaranteed to
49   be fully set up and ready to use.
50
51 ``on_search``
52
53   .. TODO: method openerp.base.SearchView.on_search
54
55   Event triggered after a user asked for a search. The search view
56   fires this event after collecting all input data (contexts, domains
57   and group_by contexts). Note that the search view does *not* merge
58   those (or otherwise evaluate them), they are returned as provided by
59   the various inputs within the view.
60
61 ``on_clear``
62
63   .. TODO: method openerp.base.SearchView.on_clear
64
65   Triggered after a user asked for a form clearing.
66
67 Input management
68 """"""""""""""""
69
70 An important concept in the search view is that of input. It is both
71 an informal protocol and an abstract type that can be inherited from.
72
73 Inputs are widgets which can contain user data (a char widget for
74 instance, or a selection box). They are capable of action and of
75 reaction:
76
77 .. _views-search-registration:
78
79 ``registration``
80
81   This is an input action. Inputs have to register themselves to the
82   main view (which they receive as a constructor argument). This is
83   performed by pushing themselves on the
84   :js:attr:`openerp.base.SearchView.inputs` array.
85
86 ``get_context``
87
88   An input reaction. When it needs to collect contexts, the view calls
89   ``get_context()`` on all its inputs.
90
91   Inputs can react in the following manners:
92
93   * Return a context (an object), this is the "normal" response if the
94     input holds a value.
95
96   * Return a value that evaluates as false (generally ``null``). This
97     value indicates the input does not contain any value and will not
98     affect the results of the search.
99
100   * Raise :js:class:`openerp.base.search.Invalid` to indicate that it
101     holds a value but this value can not be used in the search
102     (because it is incorrectly formatted or nonsensical). Raising
103     :js:class:`~openerp.base.search.Invalid` is guaranteed to cancel
104     the search process.
105
106     :js:class:`~openerp.base.search.Invalid` takes three mandatory
107     arguments: an identifier (a name for instance), the invalid value,
108     and a validation message indicating the issue.
109
110 ``get_domain``
111
112   The second input reaction, the possible behaviors of inputs are the
113   same as for ``get_context``.
114
115 The :js:class:`openerp.base.search.Input` type implements registration
116 on its own, but its implementations of ``get_context`` and
117 ``get_domain`` simply raise errors and *must* be overridden.
118
119 One last action is for filters, as an activation order has to be kept
120 on them for some controls (to establish the correct grouping sequence,
121 for instance).
122
123 To that end, filters can call
124 :js:func:`openerp.base.Search.do_toggle_filter`, providing themselves
125 as first argument.
126
127 Filters calling :js:func:`~openerp.base.Search.do_toggle_filter` also
128 need to implement a method called
129 :js:func:`~openerp.base.search.Filter.is_enabled`, which the search
130 view will use to know the current status of the filter.
131
132 The search view automatically triggers a search after calls to
133 :js:func:`~openerp.base.Search.do_toggle_filter`.
134
135 Life cycle
136 """"""""""
137
138 The search view has a pretty simple and linear life cycle, in three main steps:
139
140 :js:class:`~openerp.base.SearchView.init`
141
142   Nothing interesting happens here
143
144 :js:func:`~openerp.base.SearchView.start`
145
146   Called by the main view's creator, this is the main initialization
147   step for the list view.
148
149   It begins with a remote call to fetch the view's descriptors
150   (``fields_view_get``).
151
152   Once the remote call is complete, the ``on_loaded`` even happens,
153   holding three main operations:
154
155   :js:func:`~openerp.base.SearchView.make_widgets`
156
157     Builds and returns the top-level widgets of the search
158     view. Because it returns an array of widget lines (a 2-dimensional
159     matrix of widgets) it should be called recursively by container
160     widgets (:js:class:`openerp.base.search.Group` for instance).
161
162   :js:func:`~openerp.base.search.Widget.render`
163
164     Called by the search view on all top-level widgets. Container
165     widgets should recursively call this method on their own children
166     widgets.
167
168     Widgets are provided with a mapping of ``{name: value}`` holding
169     default values for the search view. They can freely pick their
170     initial values from there, but must pass the mapping to their
171     children widgets if they have any.
172
173   :js:func:`~openerp.base.search.Widget.start`
174
175     The last operation of the search view startup is to initialize all
176     its widgets in order. This is again done recursively (the search
177     view starts its children, which have to start their own children).
178
179 :js:func:`~openerp.base.SearchView.stop`
180
181   Used before discarding a search view, allows the search view to
182   disable its events and pass the message to its own widgets,
183   gracefully shutting down the whole view.
184
185 Widgets
186 """""""
187
188 In a search view, the widget is simply a unit of display.
189
190 All widgets must be able to react to three events, which will be
191 called in this order:
192
193 :js:func:`~openerp.base.search.Widget.render`
194
195   Called with a map of default values. The widget must return a
196   ``String``, which is its HTML representation. That string can be
197   empty (if the widget should not be represented).
198
199   Widgets are responsible for asking their children for rendering, and
200   for passing along the default values.
201
202 :js:func:`~openerp.base.search.Widget.start`
203
204   Called without arguments. At this point, the widget has been fully
205   rendered and can set its events up, if any.
206
207   The widget is responsible for starting its children, if it has any.
208
209 :js:func:`~openerp.base.search.Widget.stop`
210
211   Gives the widget the opportunity to unbind its events, remove itself
212   from the DOM and perform any other cleanup task it may have.
213
214   Even if the widget does not do anything itself, it is responsible
215   for shutting down its children.
216
217 An abstract type is available and can be inherited from, to simplify
218 the implementation of those tasks:
219
220 .. TODO: insert Widget here
221
222 .. remember to document all methods
223
224 Inputs
225 """"""
226
227 The search namespace (``openerp.base.search``) provides two more
228 abstract types, used to implement input widgets:
229
230 * :js:class:`openerp.base.search.Input` is the most basic input type,
231   it only implements :ref:`input registration
232   <views-search-registration>`.
233
234   If inherited from, descendant classes should not call its
235   implementations of :js:func:`~openerp.base.search.Input.get_context`
236   and :js:func:`~openerp.base.search.Input.get_domain`.
237
238 * :js:class:`openerp.base.search.Field` is used to implement more
239   "field" widgets (which allow the user to input potentially complex
240   values).
241
242   It provides various services for its subclasses:
243
244   * Sets up the field attributes, using attributes from the field and
245     the view node.
246
247   * It fills the widget with :js:class:`~openerp.base.search.Filter`
248     if the field has any child filter.
249
250   * It automatically generates an identifier based on the field type
251     and the field name, using
252     :js:func:`~openerp.base.search.Widget.make_id`.
253
254   * It sets up a basic (overridable)
255     :js:attr:`~openerp.base.search.Field.template` attribute, combined
256     with the previous tasks, this makes subclasses of
257     :js:class:`~openerp.base.search.Field` render themselves "for
258     free".
259
260   * It provides basic implementations of ``get_context`` and
261     ``get_domain``, both hinging on the subclasses implementing
262     ``get_value()`` (which should return a correct, converted
263     Javascript value):
264
265     :js:func:`~openerp.base.search.Field.get_context`
266
267         Checks if the field has a non-``null`` and non-empty
268         (``String``) value, and that the field has a ``context`` attr.
269
270         If both conditions are fullfilled, returns the context.
271
272     :js:func:`~openerp.base.search.Field.get_domain`
273
274         Only requires that the field has a non-``null`` and non-empty
275         value.
276
277         If the field has a ``filter_domain``, returns it
278         immediately. Otherwise, builds a context using the field's
279         name, the field :js:attr:`~openerp.base.search.Field.operator`
280         and the field value, and returns it.
281
282 .. TODO: insert Input, Field, Filter, and just about every Field subclass
283
284 List View
285 +++++++++
286
287 OpenERP Web's list views don't actually exist as such in OpenERP itself: a
288 list view is an OpenERP tree view in the ``view_mode`` form.
289
290 The overall purpose of a list view is to display collections of objects in two
291 main forms: per-object, where each object is a row in and of itself, and
292 grouped, where multiple objects are represented with a single row providing
293 an aggregated view of all grouped objects.
294
295 These two forms can be mixed within a single list view, if needed.
296
297 The root of a list view is :js:class:`openerp.base.ListView`, which may need
298 to be overridden (partially or fully) to control list behavior in non-view
299 cases (when using a list view as sub-component of a form widget for instance).
300
301 Creation and Initialization
302 """""""""""""""""""""""""""
303
304 As with most OpenERP Web views, the list view's
305 :js:func:`~openerp.base.ListView.init` takes quite a number of arguments.
306
307 While most of them are the standard view constructor arguments
308 (``view_manager``, ``session``, ``element_id``, ``dataset`` and an
309 optional ``view_id``), the list view adds a number of options for basic
310 customization (without having to override methods or templates):
311
312 ``selectable`` (default: ``true``)
313     Indicates that the list view should allow records to be selected
314     individually. Displays selection check boxes to the left of all record rows,
315     and allows for the triggering of the
316     :ref:`selection event <listview-events-selection>`.
317 ``deletable`` (default: ``true``)
318     Indicates that the list view should allow records to be removed
319     individually. Displays a deletion button to the right of all record rows,
320     and allows for the triggering of the
321     :ref:`deletion event <listview-events-deletion>`.
322 ``header`` (default: ``true``)
323     Indicates that list columns should bear a header sporting their name (for
324     non-action columns).
325 ``addable`` (default: ``"New"``)
326     Indicates that a record addition/creation button should be displayed in
327     the list's header, along with its label. Also allows for the triggering of
328     the :ref:`record addition event <listview-events-addition>`.
329 ``sortable`` (default: ``true``)
330     Indicates that the list view can be sorted per-column (by clicking on its
331     column headers).
332
333     .. TODO: event?
334 ``reorderable`` (default: ``true``)
335     Indicates that the list view records can be reordered (and re-sequenced)
336     by drag and drop.
337
338     .. TODO: event?
339
340 Events
341 """"""
342 .. _listview-events-addition:
343
344 Addition
345 ''''''''
346 The addition event is used to add a record to an existing list view. The
347 default behavior is to switch to the form view, on a new record.
348
349 Addition behavior can be overridden by replacing the
350 :js:func:`~openerp.base.ListView.do_add_record` method.
351
352 .. _listview-events-selection:
353
354 Selection
355 '''''''''
356 The selection event is triggered when a given record is selected in the list
357 view.
358
359 It can be overridden by replacing the
360 :js:func:`~openerp.base.ListView.do_select` method.
361
362 The default behavior is simply to hide or display the list-wise deletion button
363 depending on whether there are selected records or not.
364
365 .. _listview-events-deletion:
366
367 Deletion
368 ''''''''
369 The deletion event is triggered when the user tries to remove 1..n records from
370 the list view, either individually or globally (via the header button).
371
372 Deletion can be overridden by replacing the
373 :js:func:`~openerp.base.ListView.do_delete` method. By default, this method
374 calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
375 entirely.
376
377 .. note::
378
379   the list-wise deletion button (next to the record addition button)
380   simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
381   obtaining all selected record ids, but it is possible to override it
382   alone by replacing
383   :js:func:`~openerp.base.ListView.do_delete_selected`.
384
385 Internal API Doc
386 ----------------
387
388 Python
389 ++++++
390
391 These classes should be moved to other sections of the doc as needed,
392 probably.
393
394 .. automodule:: web.common.http
395     :members:
396     :undoc-members:
397
398     See also: :class:`~web.common.session.OpenERPSession`,
399     :class:`~web.common.openerplib.main.OpenERPModel`
400
401 .. automodule:: web.controllers.main
402     :members:
403     :undoc-members:
404
405 Testing
406 -------
407
408 Python
409 ++++++
410
411 Testing for the OpenERP Web core is similar to :ref:`testing addons
412 <addons-testing>`: the tests live in ``openerpweb.tests``, unittest2_
413 is the testing framework and tests can be run via either unittest2
414 (``unit2 discover``) or via nose_ (``nosetests``).
415
416 Tests for the OpenERP Web core can also be run using ``setup.py
417 test``.
418
419
420 .. _unittest2:
421     http://www.voidspace.org.uk/python/articles/unittest2.shtml
422
423 .. _nose:
424     http://somethingaboutorange.com/mrl/projects/nose/1.0.0/