[ADD] some listview doc
[odoo/odoo.git] / doc / source / addons.rst
1 Developing OpenERP Web Addons
2 =============================
3
4 An OpenERP Web addon is simply a Python package with an openerp
5 descriptor (a ``__openerp__.py`` file) which follows a few structural
6 and namespacing rules.
7
8 Structure
9 ---------
10
11 .. literalinclude:: addon-structure.txt
12
13 ``__openerp__.py``
14   The addon's descriptor, contains the following information:
15
16   ``name: str``
17     The addon name, in plain, readable english
18   ``version: str``
19     The addon version, following `Semantic Versioning`_ rules
20   ``depends: [str]``
21     A list of addons this addon needs to work correctly. ``base`` is
22     an implied dependency if the list is empty.
23   ``css: [str]``
24     An ordered list of CSS files this addon provides and needs. The
25     file paths are relative to the addon's root. Because the Web
26     Client *may* perform concatenations and other various
27     optimizations on CSS files, the order is important.
28   ``js: [str]``
29     An ordered list of Javascript files this addon provides and needs
30     (including dependencies files). As with CSS files, the order is
31     important as the Web Client *may* perform contatenations and
32     minimizations of files.
33   ``active: bool``
34     Whether this addon should be enabled by default any time it is
35     found, or whether it will be enabled through other means (on a
36     by-need or by-installation basis for instance).
37
38 ``controllers/``
39   All of the Python controllers and JSON-RPC endpoints.
40
41 ``static/``
42   The static files directory, may be served via a separate web server.
43
44 ``static/lib/``
45   Third-party libraries used by the addon.
46
47 ``static/src/{css,js,img,xml}``
48   Location for (respectively) the addon's static CSS files, its JS
49   files, its various image resources as well as the template files
50
51 ``static/test``
52   Javascript tests files
53
54 ``test/``
55   The directories in which all tests for the addon are located.
56
57 Some of these are guidelines (and not enforced by code), but it's
58 suggested that these be followed. Code which does not fit into these
59 categories can go wherever deemed suitable.
60
61 Namespacing
62 -----------
63
64 Python
65 ++++++
66
67 Because addons are also Python packages, they're inherently namespaced
68 and nothing special needs to be done on that front.
69
70 JavaScript
71 ++++++++++
72
73 The JavaScript side of an addon has to live in the namespace
74 ``openerp.$addon_name``. For instance, everything created by the addon
75 ``base`` lives in ``openerp.base``.
76
77 The root namespace of the addon is a function which takes a single
78 parameter ``openerp``, which is an OpenERP client instance. Objects
79 (as well as functions, registry instances, etc...) should be added on
80 the correct namespace on that object.
81
82 The root function will be called by the OpenERP Web client when
83 initializing the addon.
84
85 .. code-block:: javascript
86
87     // root namespace of the openerp.example addon
88     /** @namespace */
89     openerp.example = function (openerp) {
90         // basic initialization code (e.g. templates loading)
91         openerp.example.SomeClass = Class.extend(
92             /** @lends openerp.example.SomeClass# */{
93             /**
94              * Description for SomeClass's constructor here
95              *
96              * @constructs
97              */
98             init: function () {
99                 // SomeClass initialization code
100             }
101             // rest of SomeClass
102         });
103
104         // access an object in an other addon namespace to replace it
105         openerp.base.SearchView = openerp.base.SearchView.extend({
106             init: function () {
107                 this._super.apply(this, arguments);
108                 console.log('Search view initialized');
109             }
110         });
111     }
112
113 Utility behaviors
114 -----------------
115
116 JavaScript
117 ++++++++++
118
119 * All javascript objects inheriting from
120   :js:class:`openerp.base.BasicConroller` will have all methods
121   starting with ``on_`` or ``do_`` bound to their ``this``. This means
122   they don't have to be manually bound (via ``_.bind`` or ``$.proxy``)
123   in order to be useable as bound event handlers (event handlers
124   keeping their object as ``this`` rather than taking whatever
125   ``this`` object they were called with).
126
127   Beware that this is only valid for methods starting with ``do_`` and
128   ``on_``, any other method will have to be bound manually.
129
130 .. _addons-testing:
131
132 Testing
133 -------
134
135 Python
136 ++++++
137
138 OpenERP Web uses unittest2_ for its testing needs. We selected
139 unittest2 rather than unittest_ for the following reasons:
140
141 * autodiscovery_ (similar to nose, via the ``unit2``
142   CLI utility) and `pluggable test discovery`_.
143
144 * `new and improved assertions`_ (with improvements in type-specific
145   inequality reportings) including `pluggable custom types equality
146   assertions`_
147
148 * neveral new APIs, most notably `assertRaises context manager`_,
149   `cleanup function registration`_, `test skipping`_ and `class- and
150   module-level setup and teardown`_
151
152 * finally, unittest2 is a backport of Python 3's unittest. We might as
153   well get used to it.
154
155 To run tests on addons (from the root directory of OpenERP Web) is as
156 simple as typing ``PYTHONPATH=. unit2 discover -s addons`` [#]_. To
157 test an addon which does not live in the ``addons`` directory, simply
158 replace ``addons`` by the directory in which your own addon lives.
159
160 .. note:: unittest2 is entirely compatible with nose_ (or the
161      other way around). If you want to use nose as your test
162      runner (due to its addons for instance) you can simply install it
163      and run ``nosetests addons`` instead of the ``unit2`` command,
164      the result should be exactly the same.
165
166 APIs
167 ----
168
169 Javascript
170 ++++++++++
171
172 .. js:class:: openerp.base.Widget(view, node)
173
174     :param openerp.base.Controller view: The view to which the widget belongs
175     :param Object node: the ``fields_view_get`` descriptor for the widget
176
177     .. js:attribute:: $element
178
179         The widget's root element as jQuery object
180
181 .. js:class:: openerp.base.DataSet(session, model)
182
183     :param openerp.base.Session session: the RPC session object
184     :param String model: the model managed by this dataset
185
186     The DataSet is the abstraction for a sequence of records stored in
187     database.
188
189     It provides interfaces for reading records based on search
190     criteria, and for selecting and fetching records based on
191     activated ids.
192
193     .. js:function:: fetch([offset][, limit])
194
195        :param Number offset: the index from which records should start
196                              being returned (section)
197        :param Number limit: the maximum number of records to return
198        :returns: the dataset instance it was called on
199
200        Asynchronously fetches the records selected by the DataSet's
201        domain and context, in the provided sort order if any.
202
203        Only fetches the fields selected by the DataSet.
204
205        On success, triggers :js:func:`on_fetch`
206
207     .. js:function:: on_fetch(records, event)
208
209         :param Array records: an array of
210                              :js:class:`openerp.base.DataRecord`
211                              matching the DataSet's selection
212         :param event: a data holder letting the event handler fetch
213                      meta-informations about the event.
214         :type event: OnFetchEvent
215
216         Fired after :js:func:`fetch` is done fetching the records
217         selected by the DataSet.
218
219     .. js:function:: active_ids
220
221         :returns: the dataset instance it was called on
222
223         Asynchronously fetches the active records for this DataSet.
224
225         On success, triggers :js:func:`on_active_ids`
226
227     .. js:function:: on_active_ids(records)
228
229         :param Array records: an array of
230                               :js:class:`openerp.base.DataRecord`
231                               matching the currently active ids
232
233         Fired after :js:func:`active_ids` fetched the records matching
234         the DataSet's active ids.
235
236     .. js:function:: active_id
237
238         :returns: the dataset instance in was called on
239
240         Asynchronously fetches the current active record.
241
242         On success, triggers :js:func:`on_active_id`
243
244     .. js:function:: on_active_id(record)
245
246         :param Object record: the record fetched by
247                               :js:func:`active_id`, or ``null``
248         :type record: openerp.base.DataRecord
249
250         Fired after :js:func:`active_id` fetched the record matching
251         the dataset's active id
252
253     .. js:function:: set(options)
254
255         :param Object options: the options to set on the dataset
256         :type options: DataSetOptions
257         :returns: the dataset instance it was called on
258
259         Configures the data set by setting various properties on it
260
261     .. js:function:: prev
262
263         :returns: the dataset instance it was called on
264
265         Activates the id preceding the current one in the active ids
266         sequence of the dataset.
267
268         If the current active id is at the start of the sequence,
269         wraps back to the last id of the sequence.
270
271     .. js:function:: next
272
273         :returns: the dataset instance it was called on
274
275         Activates the id following the current one in the active ids
276         sequence.
277
278         If the current active id is the last of the sequence, wraps
279         back to the beginning of the active ids sequence.
280
281     .. js:function:: select(ids)
282
283         :param Array ids: the identifiers to activate on the dataset
284         :returns: the dataset instance it was called on
285
286         Activates all the ids specified in the dataset, resets the
287         current active id to be the first id of the new sequence.
288
289         The internal order will be the same as the ids list provided.
290
291     .. js:function:: get_active_ids
292
293         :returns: the list of current active ids for the dataset
294
295     .. js:function:: activate(id)
296
297         :param Number id: the id to activate
298         :returns: the dataset instance it was called on
299
300         Activates the id provided in the dataset. If no ids are
301         selected, selects the id in the dataset.
302
303         If ids are already selected and the provided id is not in that
304         selection, raises an error.
305
306     .. js:function:: get_active_id
307
308         :returns: the dataset's current active id
309
310 Ad-hoc objects and structural types
311 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
312
313 These objects are not associated with any specific class, they're
314 generally literal objects created on the spot. Names are merely
315 convenient ways to refer to them and their properties.
316
317 .. js:class:: OnFetchEvent
318
319     .. js:attribute:: context
320
321         The context used for the :js:func:`fetch` call (domain set on
322         the :js:class:`openerp.base.DataSet` when ``fetch`` was
323         called)
324
325     .. js:attribute:: domain
326
327         The domain used for the :js:func:`fetch` call
328
329     .. js:attribute:: limit
330
331         The limit with which the original :js:func:`fetch` call was
332         performed
333
334     .. js:attribute:: offset
335
336         The offset with which the original :js:func:`fetch` call was
337         performed
338
339     .. js:attribute:: sort
340
341        The sorting criteria active on the
342        :js:class:`openerp.base.DataSet` when :js:func:`fetch` was
343        called
344
345 .. js:class:: DataSetOptions
346
347     .. js:attribute:: context
348
349     .. js:attribute:: domain
350
351     .. js:attribute:: sort
352
353 Python
354 ++++++
355
356 .. autoclass:: openerpweb.openerpweb.OpenERPSession
357     :members:
358
359 .. autoclass:: openerpweb.openerpweb.OpenERPModel
360     :members:
361
362 * Addons lifecycle (loading, execution, events, ...)
363
364   * Python-side
365   * JS-side
366
367 * Handling static files
368 * Overridding a Python controller (object?)
369 * Overridding a Javascript controller (object?)
370 * Extending templates
371   .. how do you handle deploying static files via e.g. a separate lighttpd?
372 * Python public APIs
373 * QWeb templates description?
374 * OpenERP Web modules (from OpenERP modules)
375
376 .. [#] the ``-s`` parameter tells ``unit2`` to start trying to
377        find tests in the provided directory (here we're testing
378        addons). However a side-effect of that is to set the
379        ``PYTHONPATH`` there as well, so it will fail to find (and
380        import) ``openerpweb``.
381
382        The ``-t`` parameter lets us set the ``PYTHONPATH``
383        independently, but it doesn't accept multiple values and here
384        we really want to have both ``.`` and ``addons`` on the
385        ``PYTHONPATH``.
386
387        The solution is to set the ``PYTHONPATH`` to ``.`` on start,
388        and the ``start-directory`` to ``addons``. This results in a
389        correct ``PYTHONPATH`` within ``unit2``.
390
391 .. _unittest:
392     http://docs.python.org/library/unittest.html
393
394 .. _unittest2:
395     http://www.voidspace.org.uk/python/articles/unittest2.shtml
396
397 .. _autodiscovery:
398     http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-discovery
399
400 .. _pluggable test discovery:
401     http://www.voidspace.org.uk/python/articles/unittest2.shtml#load-tests
402
403 .. _new and improved assertions:
404     http://www.voidspace.org.uk/python/articles/unittest2.shtml#new-assert-methods
405
406 .. _pluggable custom types equality assertions:
407     http://www.voidspace.org.uk/python/articles/unittest2.shtml#add-new-type-specific-functions
408
409 .. _assertRaises context manager:
410     http://www.voidspace.org.uk/python/articles/unittest2.shtml#assertraises
411
412 .. _cleanup function registration:
413     http://www.voidspace.org.uk/python/articles/unittest2.shtml#cleanup-functions-with-addcleanup
414
415 .. _test skipping:
416     http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-skipping
417
418 .. _class- and module-level setup and teardown:
419     http://www.voidspace.org.uk/python/articles/unittest2.shtml#class-and-module-level-fixtures
420
421 .. _Semantic Versioning:
422     http://semver.org/
423
424 .. _nose:
425     http://somethingaboutorange.com/mrl/projects/nose/1.0.0/