rm files, move postload hook
[odoo/odoo.git] / addons / web / doc / rpc.rst
1 Outside the box: network interactions
2 =====================================
3
4 Building static displays is all nice and good and allows for neat
5 effects (and sometimes you're given data to display from third parties
6 so you don't have to make any effort), but a point generally comes
7 where you'll want to talk to the world and make some network requests.
8
9 OpenERP Web provides two primary APIs to handle this, a low-level
10 JSON-RPC based API communicating with the Python section of OpenERP
11 Web (and of your addon, if you have a Python part) and a high-level
12 API above that allowing your code to talk directly to the OpenERP
13 server, using familiar-looking calls.
14
15 All networking APIs are :doc:`asynchronous </async>`. As a result, all
16 of them will return :js:class:`Deferred` objects (whether they resolve
17 those with values or not). Understanding how those work before before
18 moving on is probably necessary.
19
20 High-level API: calling into OpenERP models
21 -------------------------------------------
22
23 Access to OpenERP object methods (made available through XML-RPC from
24 the server) is done via the :js:class:`openerp.web.Model` class. This
25 class maps onto the OpenERP server objects via two primary methods,
26 :js:func:`~openerp.web.Model.call` and
27 :js:func:`~openerp.web.Model.query`.
28
29 :js:func:`~openerp.web.Model.call` is a direct mapping to the
30 corresponding method of the OpenERP server object. Its usage is
31 similar to that of the OpenERP Model API, with three differences:
32
33 * The interface is :doc:`asynchronous </async>`, so instead of
34   returning results directly RPC method calls will return
35   :js:class:`Deferred` instances, which will themselves resolve to the
36   result of the matching RPC call.
37
38 * Because ECMAScript 3/Javascript 1.5 doesnt feature any equivalent to
39   ``__getattr__`` or ``method_missing``, there needs to be an explicit
40   method to dispatch RPC methods.
41
42 * No notion of pooler, the model proxy is instantiated where needed,
43   not fetched from an other (somewhat global) object
44
45 .. code-block:: javascript
46
47     var Users = new Model('res.users');
48
49     Users.call('change_password', ['oldpassword', 'newpassword'],
50                       {context: some_context}).then(function (result) {
51         // do something with change_password result
52     });
53
54 :js:func:`~openerp.web.Model.query` is a shortcut for a builder-style
55 interface to searches (``search`` + ``read`` in OpenERP RPC terms). It
56 returns a :js:class:`~openerp.web.Query` object which is immutable but
57 allows building new :js:class:`~openerp.web.Query` instances from the
58 first one, adding new properties or modifiying the parent object's:
59
60 .. code-block:: javascript
61
62     Users.query(['name', 'login', 'user_email', 'signature'])
63          .filter([['active', '=', true], ['company_id', '=', main_company]])
64          .limit(15)
65          .all().then(function (users) {
66         // do work with users records
67     });
68
69 The query is only actually performed when calling one of the query
70 serialization methods, :js:func:`~openerp.web.Query.all` and
71 :js:func:`~openerp.web.Query.first`. These methods will perform a new
72 RPC call every time they are called.
73
74 For that reason, it's actually possible to keep "intermediate" queries
75 around and use them differently/add new specifications on them.
76
77 .. js:class:: openerp.web.Model(name)
78
79     .. js:attribute:: openerp.web.Model.name
80
81         name of the OpenERP model this object is bound to
82
83     .. js:function:: openerp.web.Model.call(method[, args][, kwargs])
84
85          Calls the ``method`` method of the current model, with the
86          provided positional and keyword arguments.
87
88          :param String method: method to call over rpc on the
89                                :js:attr:`~openerp.web.Model.name`
90          :param Array<> args: positional arguments to pass to the
91                               method, optional
92          :param Object<> kwargs: keyword arguments to pass to the
93                                  method, optional
94          :rtype: Deferred<>         
95
96     .. js:function:: openerp.web.Model.query(fields)
97
98          :param Array<String> fields: list of fields to fetch during
99                                       the search
100          :returns: a :js:class:`~openerp.web.Query` object
101                    representing the search to perform
102
103 .. js:class:: openerp.web.Query(fields)
104
105     The first set of methods is the "fetching" methods. They perform
106     RPC queries using the internal data of the object they're called
107     on.
108
109     .. js:function:: openerp.web.Query.all()
110
111         Fetches the result of the current
112         :js:class:`~openerp.web.Query` object's search.
113
114         :rtype: Deferred<Array<>>
115
116     .. js:function:: openerp.web.Query.first()
117
118        Fetches the **first** result of the current
119        :js:class:`~openerp.web.Query`, or ``null`` if the current
120        :js:class:`~openerp.web.Query` does have any result.
121
122        :rtype: Deferred<Object | null>
123
124     .. js:function:: openerp.web.Query.count()
125
126        Fetches the number of records the current
127        :js:class:`~openerp.web.Query` would retrieve.
128
129        :rtype: Deferred<Number>
130
131     .. js:function:: openerp.web.Query.group_by(grouping...)
132
133        Fetches the groups for the query, using the first specified
134        grouping parameter
135
136        :param Array<String> grouping: Lists the levels of grouping
137                                       asked of the server. Grouping
138                                       can actually be an array or
139                                       varargs.
140        :rtype: Deferred<Array<openerp.web.QueryGroup>> | null
141
142     The second set of methods is the "mutator" methods, they create a
143     **new** :js:class:`~openerp.web.Query` object with the relevant
144     (internal) attribute either augmented or replaced.
145
146     .. js:function:: openerp.web.Query.context(ctx)
147
148        Adds the provided ``ctx`` to the query, on top of any existing
149        context
150
151     .. js:function:: openerp.web.Query.filter(domain)
152
153        Adds the provided domain to the query, this domain is
154        ``AND``-ed to the existing query domain.
155
156     .. js:function:: opeenrp.web.Query.offset(offset)
157
158        Sets the provided offset on the query. The new offset
159        *replaces* the old one.
160
161     .. js:function:: openerp.web.Query.limit(limit)
162
163        Sets the provided limit on the query. The new limit *replaces*
164        the old one.
165
166     .. js:function:: openerp.web.Query.order_by(fields…)
167
168        Overrides the model's natural order with the provided field
169        specifications. Behaves much like Django's `QuerySet.order_by
170        <https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by>`_:
171
172        * Takes 1..n field names, in order of most to least importance
173          (the first field is the first sorting key). Fields are
174          provided as strings.
175
176        * A field specifies an ascending order, unless it is prefixed
177          with the minus sign "``-``" in which case the field is used
178          in the descending order
179
180        Divergences from Django's sorting include a lack of random sort
181        (``?`` field) and the inability to "drill down" into relations
182        for sorting.
183
184 Aggregation (grouping)
185 ~~~~~~~~~~~~~~~~~~~~~~
186
187 OpenERP has powerful grouping capacities, but they are kind-of strange
188 in that they're recursive, and level n+1 relies on data provided
189 directly by the grouping at level n. As a result, while ``read_group``
190 works it's not a very intuitive API.
191
192 OpenERP Web 7.0 eschews direct calls to ``read_group`` in favor of
193 calling a method of :js:class:`~openerp.web.Query`, `much in the way
194 it is one in SQLAlchemy
195 <http://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.group_by>`_ [#]_:
196
197 .. code-block:: javascript
198
199     some_query.group_by(['field1', 'field2']).then(function (groups) {
200         // do things with the fetched groups
201     });
202
203 This method is asynchronous when provided with 1..n fields (to group
204 on) as argument, but it can also be called without any field (empty
205 fields collection or nothing at all). In this case, instead of
206 returning a Deferred object it will return ``null``.
207
208 When grouping criterion come from a third-party and may or may not
209 list fields (e.g. could be an empty list), this provides two ways to
210 test the presence of actual subgroups (versus the need to perform a
211 regular query for records):
212
213 * A check on ``group_by``'s result and two completely separate code
214   paths
215
216   .. code-block:: javascript
217
218       var groups;
219       if (groups = some_query.group_by(gby)) {
220           groups.then(function (gs) {
221               // groups
222           });
223       }
224       // no groups
225
226 * Or a more coherent code path using :js:func:`when`'s ability to
227   coerce values into deferreds:
228
229   .. code-block:: javascript
230
231       $.when(some_query.group_by(gby)).then(function (groups) {
232           if (!groups) {
233               // No grouping
234           } else {
235               // grouping, even if there are no groups (groups
236               // itself could be an empty array)
237           }
238       });
239
240 The result of a (successful) :js:func:`~openerp.web.Query.group_by` is
241 an array of :js:class:`~openerp.web.QueryGroup`.
242
243 Low-level API: RPC calls to Python side
244 ---------------------------------------
245
246 While the previous section is great for calling core OpenERP code
247 (models code), it does not work if you want to call the Python side of
248 OpenERP Web.
249
250 For this, a lower-level API exists on on
251 :js:class:`~openerp.web.Connection` objects (usually available through
252 ``openerp.connection``): the ``rpc`` method.
253
254 This method simply takes an absolute path (which is the combination of
255 the Python controller's ``_cp_path`` attribute and the name of the
256 method you want to call) and a mapping of attributes to values (applied
257 as keyword arguments on the Python method [#]_). This function fetches
258 the return value of the Python methods, converted to JSON.
259
260 For instance, to call the ``eval_domain_and_context`` of the
261 :class:`~web.controllers.main.Session` controller:
262
263 .. code-block:: javascript
264
265     openerp.connection.rpc('/web/session/eval_domain_and_context', {
266         domains: ds,
267         contexts: cs
268     }).then(function (result) {
269         // handle result
270     });
271
272 .. [#] with a small twist: SQLAlchemy's ``orm.query.Query.group_by``
273        is not terminal, it returns a query which can still be altered.
274
275 .. [#] except for ``context``, which is extracted and stored in the
276        request object itself.