1 Developing OpenERP Web Addons
2 =============================
7 .. literalinclude:: addon-structure.txt
10 The addon's descriptor, contains the following information:
13 The addon name, in plain, readable english
15 The addon version, following `Semantic Versioning`_ rules
17 A list of addons this addon needs to work correctly. ``base`` is
18 an implied dependency if the list is empty.
20 An ordered list of CSS files this addon provides and needs. The
21 file paths are relative to the addon's root. Because the Web
22 Client *may* perform concatenations and other various
23 optimizations on CSS files, the order is important.
25 An ordered list of Javascript files this addon provides and needs
26 (including dependencies files). As with CSS files, the order is
27 important as the Web Client *may* perform contatenations and
28 minimizations of files.
30 Whether this addon should be enabled by default any time it is
31 found, or whether it will be enabled through other means (on a
32 by-need or by-installation basis for instance).
35 All of the Python controllers and JSON-RPC endpoints.
38 The static files directory, may be served via a separate web server.
40 The third-party dependencies should be bundled in it (each in their
44 Sub-tree for all the addon's own static files.
46 ``static/openerp/{css,js,img}``
47 Location for (respectively) the addon's static CSS files, its JS
48 files and its various image resources.
51 The directories in which all tests for the addon are located.
61 OpenERP Web uses unittest2_ for its testing needs. We selected
62 unittest2 rather than unittest_ for the following reasons:
64 * autodiscovery_ (similar to nose, via the ``unit2``
65 CLI utility) and `pluggable test discovery`_.
67 * `new and improved assertions`_ (with improvements in type-specific
68 inequality reportings) including `pluggable custom types equality
71 * neveral new APIs, most notably `assertRaises context manager`_,
72 `cleanup function registration`_, `test skipping`_ and `class- and
73 module-level setup and teardown`_
75 * finally, unittest2 is a backport of Python 3's unittest. We might as
78 To run tests on addons (from the root directory of OpenERP Web) is as
79 simple as typing ``PYTHONPATH=. unit2 discover -s addons`` [#]_. To
80 test an addon which does not live in the ``addons`` directory, simply
81 replace ``addons`` by the directory in which your own addon lives.
83 .. note:: unittest2 is entirely compatible with nose_ (or the
84 other way around). If you want to use nose as your test
85 runner (due to its addons for instance) you can simply install it
86 and run ``nosetests addons`` instead of the ``unit2`` command,
87 the result should be exactly the same.
95 .. js:class:: openerp.base.Widget(view, node)
97 :param openerp.base.Controller view: The view to which the widget belongs
98 :param Object node: the ``fields_view_get`` descriptor for the widget
100 .. js:attribute:: $element
102 The widget's root element as jQuery object
104 .. js:class:: openerp.base.DataSet(session, model)
106 :param openerp.base.Session session: the RPC session object
107 :param String model: the model managed by this dataset
109 The DataSet is the abstraction for a sequence of records stored in
112 It provides interfaces for reading records based on search
113 criteria, and for selecting and fetching records based on
116 .. js:function:: fetch([offset][, limit])
118 :param Number offset: the index from which records should start
119 being returned (section)
120 :param Number limit: the maximum number of records to return
121 :returns: the dataset instance it was called on
123 Asynchronously fetches the records selected by the DataSet's
124 domain and context, in the provided sort order if any.
126 Only fetches the fields selected by the DataSet.
128 On success, triggers :js:func:`on_fetch`
130 .. js:function:: on_fetch(records, event)
132 :param Array records: an array of
133 :js:class:`openerp.base.DataRecord`
134 matching the DataSet's selection
135 :param event: a data holder letting the event handler fetch
136 meta-informations about the event.
137 :type event: OnFetchEvent
139 Fired after :js:func:`fetch` is done fetching the records
140 selected by the DataSet.
142 .. js:function:: active_ids
144 :returns: the dataset instance it was called on
146 Asynchronously fetches the active records for this DataSet.
148 On success, triggers :js:func:`on_active_ids`
150 .. js:function:: on_active_ids(records)
152 :param Array records: an array of
153 :js:class:`openerp.base.DataRecord`
154 matching the currently active ids
156 Fired after :js:func:`active_ids` fetched the records matching
157 the DataSet's active ids.
159 .. js:function:: active_id
161 :returns: the dataset instance in was called on
163 Asynchronously fetches the current active record.
165 On success, triggers :js:func:`on_active_id`
167 .. js:function:: on_active_id(record)
169 :param Object record: the record fetched by
170 :js:func:`active_id`, or ``null``
171 :type record: openerp.base.DataRecord
173 Fired after :js:func:`active_id` fetched the record matching
174 the dataset's active id
176 .. js:function:: set(options)
178 :param Object options: the options to set on the dataset
179 :type options: DataSetOptions
180 :returns: the dataset instance it was called on
182 Configures the data set by setting various properties on it
184 .. js:function:: prev
186 :returns: the dataset instance it was called on
188 Activates the id preceding the current one in the active ids
189 sequence of the dataset.
191 If the current active id is at the start of the sequence,
192 wraps back to the last id of the sequence.
194 .. js:function:: next
196 :returns: the dataset instance it was called on
198 Activates the id following the current one in the active ids
201 If the current active id is the last of the sequence, wraps
202 back to the beginning of the active ids sequence.
204 .. js:function:: select(ids)
206 :param Array ids: the identifiers to activate on the dataset
207 :returns: the dataset instance it was called on
209 Activates all the ids specified in the dataset, resets the
210 current active id to be the first id of the new sequence.
212 The internal order will be the same as the ids list provided.
214 .. js:function:: get_active_ids
216 :returns: the list of current active ids for the dataset
218 .. js:function:: activate(id)
220 :param Number id: the id to activate
221 :returns: the dataset instance it was called on
223 Activates the id provided in the dataset. If no ids are
224 selected, selects the id in the dataset.
226 If ids are already selected and the provided id is not in that
227 selection, raises an error.
229 .. js:function:: get_active_id
231 :returns: the dataset's current active id
233 .. js:class:: openerp.base.DataRecord(session, model, fields, values)
235 Ad-hoc objects and structural types
236 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238 These objects are not associated with any specific class, they're
239 generally literal objects created on the spot. Names are merely
240 convenient ways to refer to them and their properties.
242 .. js:class:: OnFetchEvent
244 .. js:attribute:: context
246 The context used for the :js:func:`fetch` call (domain set on
247 the :js:class:`openerp.base.DataSet` when ``fetch`` was
250 .. js:attribute:: domain
252 The domain used for the :js:func:`fetch` call
254 .. js:attribute:: limit
256 The limit with which the original :js:func:`fetch` call was
259 .. js:attribute:: offset
261 The offset with which the original :js:func:`fetch` call was
264 .. js:attribute:: sort
266 The sorting criteria active on the
267 :js:class:`openerp.base.DataSet` when :js:func:`fetch` was
270 .. js:class:: DataSetOptions
272 .. js:attribute:: context
274 .. js:attribute:: domain
276 .. js:attribute:: sort
281 .. autoclass:: openerpweb.openerpweb.OpenERPSession
284 .. autoclass:: openerpweb.openerpweb.OpenERPModel
287 * Addons lifecycle (loading, execution, events, ...)
292 * Handling static files
293 * Overridding a Python controller (object?)
294 * Overridding a Javascript controller (object?)
295 * Extending templates
296 .. how do you handle deploying static files via e.g. a separate lighttpd?
298 * QWeb templates description?
299 * OpenERP Web modules (from OpenERP modules)
301 .. [#] the ``-s`` parameter tells ``unit2`` to start trying to
302 find tests in the provided directory (here we're testing
303 addons). However a side-effect of that is to set the
304 ``PYTHONPATH`` there as well, so it will fail to find (and
305 import) ``openerpweb``.
307 The ``-t`` parameter lets us set the ``PYTHONPATH``
308 independently, but it doesn't accept multiple values and here
309 we really want to have both ``.`` and ``addons`` on the
312 The solution is to set the ``PYTHONPATH`` to ``.`` on start,
313 and the ``start-directory`` to ``addons``. This results in a
314 correct ``PYTHONPATH`` within ``unit2``.
317 http://docs.python.org/library/unittest.html
320 http://www.voidspace.org.uk/python/articles/unittest2.shtml
323 http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-discovery
325 .. _pluggable test discovery:
326 http://www.voidspace.org.uk/python/articles/unittest2.shtml#load-tests
328 .. _new and improved assertions:
329 http://www.voidspace.org.uk/python/articles/unittest2.shtml#new-assert-methods
331 .. _pluggable custom types equality assertions:
332 http://www.voidspace.org.uk/python/articles/unittest2.shtml#add-new-type-specific-functions
334 .. _assertRaises context manager:
335 http://www.voidspace.org.uk/python/articles/unittest2.shtml#assertraises
337 .. _cleanup function registration:
338 http://www.voidspace.org.uk/python/articles/unittest2.shtml#cleanup-functions-with-addcleanup
341 http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-skipping
343 .. _class- and module-level setup and teardown:
344 http://www.voidspace.org.uk/python/articles/unittest2.shtml#class-and-module-level-fixtures
346 .. _Semantic Versioning:
350 http://somethingaboutorange.com/mrl/projects/nose/1.0.0/