[IMP] css forum
[odoo/odoo.git] / doc / api_integration.rst
1 :classes: stripe
2
3 ===========
4 Web Service
5 ===========
6
7 Odoo is mostly extended internally via modules, but much of its features and
8 all of its data is also available from the outside for external analysis or
9 integration with various tools. Part of the :ref:`reference/orm/model` API is
10 easily available over XML-RPC_ and accessible from a variety of languages.
11
12 .. Odoo XML-RPC idiosyncracies:
13    * uses multiple endpoint and a nested call syntax instead of a
14      "hierarchical" server structure (e.g. ``openerp.res.partner.read()``)
15    * uses its own own manual auth system instead of basic auth or sessions
16      (basic is directly supported the Python and Ruby stdlibs as well as
17      ws-xmlrpc, not sure about ripcord)
18    * own auth is inconvenient as (uid, password) have to be explicitly passed
19      into every call. Session would allow db to be stored as well
20    These issues are especially visible in Java, somewhat less so in PHP
21
22 Connection and authentication
23 =============================
24
25 .. kinda gross because it duplicates existing bits
26
27 .. only:: html
28
29     .. rst-class:: setupcode hidden
30
31         .. code-block:: python
32
33             import xmlrpclib
34             info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
35             url, db, username, password = \
36                 info['host'], info['database'], info['user'], info['password']
37             common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
38             uid = common.authenticate(db, username, password, {})
39             models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
40
41         .. code-block:: ruby
42
43             require "xmlrpc/client"
44             info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
45             url, db, username, password = \
46                 info['host'], info['database'], info['user'], info['password']
47             common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
48             uid = common.call('authenticate', db, username, password, {})
49             models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
50
51         .. code-block:: php
52
53             require_once('ripcord.php');
54             $info = ripcord::client('https://demo.odoo.com/start')->start();
55             list($url, $db, $username, $password) =
56               array($info['host'], $info['database'], $info['user'], $info['password']);
57             $common = ripcord::client("$url/xmlrpc/2/common");
58             $uid = $common->authenticate($db, $username, $password, array());
59             $models = ripcord::client("$url/xmlrpc/2/object");
60
61         .. code-block:: java
62
63             final XmlRpcClient client = new XmlRpcClient();
64
65             final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
66             start_config.setServerURL(new URL("https://demo.odoo.com/start"));
67             final Map<String, String> info = (Map<String, String>)client.execute(
68                 start_config, "start", Collections.emptyList());
69
70             final String url = info.get("host"),
71                           db = info.get("database"),
72                     username = info.get("user"),
73                     password = info.get("password");
74
75             final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
76             common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
77
78             int uid = (int)client.execute(
79                 common_config, "authenticate", Arrays.asList(
80                     db, username, password, Collections.emptyMap()));
81
82             final XmlRpcClient models = new XmlRpcClient() {{
83                 setConfig(new XmlRpcClientConfigImpl() {{
84                     setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
85                 }});
86             }};
87
88 Configuration
89 -------------
90
91 If you already have an Odoo server installed, you can just use its
92 parameters
93
94 .. rst-class:: switchable setup
95
96     .. code-block:: python
97
98         url = <insert server URL>
99         db = <insert database name>
100         username = 'admin'
101         password = <insert password for your admin user (default: admin)>
102
103     .. code-block:: ruby
104
105         url = <insert server URL>
106         db = <insert database name>
107         username = "admin"
108         password = <insert password for your admin user (default: admin)>
109
110     .. code-block:: php
111
112         $url = <insert server URL>;
113         $db = <insert database name>;
114         $username = "admin";
115         $password = <insert password for your admin user (default: admin)>;
116
117     .. code-block:: java
118
119         final String url = <insert server URL>,
120                       db = <insert database name>,
121                 username = "admin",
122                 password = <insert password for your admin user (default: admin)>;
123
124 To make exploration simpler, you can also ask https://demo.odoo.com for a test
125 database:
126
127 .. rst-class:: switchable setup
128
129     .. code-block:: python
130
131         import xmlrpclib
132         info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
133         url, db, username, password = \
134             info['host'], info['database'], info['user'], info['password']
135
136     .. code-block:: ruby
137
138         require "xmlrpc/client"
139         info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
140         url, db, username, password = \
141             info['host'], info['database'], info['user'], info['password']
142
143     .. code-block:: php
144
145         require_once('ripcord.php');
146         $info = ripcord::client('https://demo.odoo.com/start')->start();
147         list($url, $db, $username, $password) =
148           array($info['host'], $info['database'], $info['user'], $info['password']);
149
150     .. code-block:: java
151
152         final XmlRpcClient client = new XmlRpcClient();
153
154         final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
155         start_config.setServerURL(new URL("https://demo.odoo.com/start"));
156         final Map<String, String> info = (Map<String, String>)client.execute(
157             start_config, "start", Collections.emptyList());
158
159         final String url = info.get("host"),
160                       db = info.get("database"),
161                 username = info.get("user"),
162                 password = info.get("password");
163
164 .. rst-class:: force-right
165
166     .. note::
167         :class: only-php
168
169         These examples use the `Ripcord <https://code.google.com/p/ripcord/>`_
170         library, which provides a simple XML-RPC API. Ripcord requires that
171         `XML-RPC support be enabled
172         <http://php.net/manual/en/xmlrpc.installation.php>`_ in your PHP
173         installation.
174
175         Since calls are performed over
176         `HTTPS <http://en.wikipedia.org/wiki/HTTP_Secure>`_, it also requires that
177         the `OpenSSL extension
178         <http://php.net/manual/en/openssl.installation.php>`_ be enabled.
179
180     .. note::
181         :class: only-java
182
183         These examples use the `Apache XML-RPC library
184         <https://ws.apache.org/xmlrpc/>`_
185
186 Logging in
187 ----------
188
189 Odoo requires users of the API to be authenticated before being able to query
190 much data.
191
192 The ``xmlrpc/2/common`` endpoint provides meta-calls which don't require
193 authentication, such as the authentication itself or fetching version
194 information. To verify if the connection information is correct before trying
195 to authenticate, the simplest call is to ask for the server's version. The
196 authentication itself is done through the ``authenticate`` function and
197 returns a user identifier (``uid``) used in authenticated calls instead of
198 the login.
199
200 .. rst-class:: switchable setup
201
202     .. code-block:: python
203
204         common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
205         common.version()
206
207     .. code-block:: ruby
208
209         common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
210         common.call('version')
211
212     .. code-block:: php
213
214         $common = ripcord::client("$url/xmlrpc/2/common");
215         $common->version();
216
217     .. code-block:: java
218
219         final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
220         common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
221         client.execute(common_config, "version", Collections.emptyList());
222
223 .. code-block:: json
224
225     {
226         "server_version": "8.0",
227         "server_version_info": [8, 0, 0, "final", 0],
228         "server_serie": "8.0",
229         "protocol_version": 1,
230     }
231
232 .. rst-class:: switchable setup
233
234     .. code-block:: python
235
236         uid = common.authenticate(db, username, password, {})
237
238     .. code-block:: ruby
239
240         uid = common.call('authenticate', db, username, password, {})
241
242     .. code-block:: php
243
244         $uid = $common->authenticate($db, $username, $password, array());
245
246     .. code-block:: java
247
248         int uid = (int)client.execute(
249             common_config, "authenticate", Arrays.asList(
250                 db, username, password, Collections.emptyMap()));
251
252 Calling methods
253 ===============
254
255 The second endpoint is ``xmlrpc/2/object``, is used to call methods of odoo
256 models via the ``execute_kw`` RPC function.
257
258 Each call to ``execute_kw`` takes the following parameters:
259
260 * the database to use, a string
261 * the user id (retrieved through ``authenticate``), an integer
262 * the user's password, a string
263 * the model name, a string
264 * the method name, a string
265 * an array/list of parameters passed by position
266 * a mapping/dict of parameters to pass by keyword (optional)
267
268 .. rst-class:: force-right
269
270 For instance to see if we can read the ``res.partner`` model we can call
271 ``check_access_rights`` with ``operation`` passed by position and
272 ``raise_exception`` passed by keyword (in order to get a true/false result
273 rather than true/error):
274
275 .. rst-class:: switchable setup
276
277     .. code-block:: python
278
279         models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
280         models.execute_kw(db, uid, password,
281             'res.partner', 'check_access_rights',
282             ['read'], {'raise_exception': False})
283
284     .. code-block:: ruby
285
286         models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
287         models.execute_kw(db, uid, password,
288             'res.partner', 'check_access_rights',
289             ['read'], {raise_exception: false})
290
291     .. code-block:: php
292
293         $models = ripcord::client("$url/xmlrpc/2/object");
294         $models->execute_kw($db, $uid, $password,
295             'res.partner', 'check_access_rights',
296             array('read'), array('raise_exception' => false));
297
298     .. code-block:: java
299
300         final XmlRpcClient models = new XmlRpcClient() {{
301             setConfig(new XmlRpcClientConfigImpl() {{
302                 setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
303             }});
304         }};
305         models.execute("execute_kw", Arrays.asList(
306             db, uid, password,
307             "res.partner", "check_access_rights",
308             Arrays.asList("read"),
309             new HashMap() {{ put("raise_exception", false); }}
310         ));
311
312 .. code-block:: json
313
314     true
315
316 .. todo:: this should be runnable and checked
317
318 List records
319 ------------
320
321 Records can be listed and filtered via :meth:`~openerp.models.Model.search`.
322
323 :meth:`~openerp.models.Model.search` takes a mandatory
324 :ref:`domain <reference/orm/domains>` filter (possibly empty), and returns the
325 database identifiers of all records matching the filter. To list customer
326 companies for instance:
327
328 .. rst-class:: switchable
329
330     .. code-block:: python
331
332         models.execute_kw(db, uid, password,
333             'res.partner', 'search',
334             [[['is_company', '=', True], ['customer', '=', True]]])
335
336     .. code-block:: ruby
337
338         models.execute_kw(db, uid, password,
339             'res.partner', 'search',
340             [[['is_company', '=', true], ['customer', '=', true]]])
341
342     .. code-block:: php
343
344         $domain = array(array('is_company', '=', true),
345                         array('customer', '=', true));
346         $models->execute_kw($db, $uid, $password,
347             'res.partner', 'search', array($domain));
348
349     .. code-block:: java
350
351         final List domain = Arrays.asList(
352             Arrays.asList("is_company", "=", true),
353             Arrays.asList("customer", "=", true));
354         Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
355             db, uid, password,
356             "res.partner", "search",
357             Arrays.asList(domain)
358         )));
359
360 .. code-block:: json
361
362     [7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74]
363
364 Pagination
365 ''''''''''
366
367 By default a research will return the ids of all records matching the
368 condition, which may be a huge number. ``offset`` and ``limit`` parameters are
369 available to only retrieve a subset of all matched records.
370
371 .. rst-class:: switchable
372
373     .. code-block:: python
374
375         models.execute_kw(db, uid, password,
376             'res.partner', 'search',
377             [[['is_company', '=', True], ['customer', '=', True]]],
378             {'offset': 10, 'limit': 5})
379
380     .. code-block:: ruby
381
382         models.execute_kw(db, uid, password,
383             'res.partner', 'search',
384             [[['is_company', '=', true], ['customer', '=', true]]],
385             {offset: 10, limit: 5})
386
387     .. code-block:: php
388
389         $models->execute_kw($db, $uid, $password,
390             'res.partner', 'search',
391             array($domain),
392             array('offset'=>10, 'limit'=>5));
393
394     .. code-block:: java
395
396         Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
397             db, uid, password,
398             "res.partner", "search",
399             Arrays.asList(domain),
400             new HashMap() {{ put("offset", 10); put("limit", 5); }}
401         )));
402
403 .. code-block:: json
404
405     [13, 20, 30, 22, 29]
406
407 Count records
408 -------------
409
410 Rather than retrieve a possibly gigantic list of records and count them
411 afterwards, :meth:`~openerp.models.Model.search_count` can be used to retrieve
412 only the number of records matching the query. It takes the same
413 :ref:`domain <reference/orm/domains>` filter as
414 :meth:`~openerp.models.Model.search` and no other parameter.
415
416 .. rst-class:: switchable
417
418     .. code-block:: python
419
420         models.execute_kw(db, uid, password,
421             'res.partner', 'search_count',
422             [[['is_company', '=', True], ['customer', '=', True]]])
423
424     .. code-block:: ruby
425
426         models.execute_kw(db, uid, password,
427             'res.partner', 'search_count',
428             [[['is_company', '=', true], ['customer', '=', true]]])
429
430     .. code-block:: php
431
432         $models->execute_kw($db, $uid, $password,
433             'res.partner', 'search_count',
434             array($domain));
435
436     .. code-block:: java
437
438         (Integer)models.execute("execute_kw", Arrays.asList(
439             db, uid, password,
440             "res.partner", "search_count",
441             Arrays.asList(domain)
442         ));
443
444 .. code-block:: json
445
446     19
447
448 .. warning::
449
450     calling ``search`` then ``search_count`` (or the other way around) may not
451     yield coherent results if other users are using the server: stored data
452     could have changed between the calls
453
454 Read records
455 ------------
456
457 Record data is accessible via the :meth:`~openerp.models.Model.read` method,
458 which takes a list of ids (as returned by
459 :meth:`~openerp.models.Model.search`) and optionally a list of fields to
460 fetch. By default, it will fetch all the fields the current user can read,
461 which tends to be a huge amount.
462
463 .. rst-class:: switchable
464
465     .. code-block:: python
466
467         ids = models.execute_kw(db, uid, password,
468             'res.partner', 'search',
469             [[['is_company', '=', True], ['customer', '=', True]]],
470             {'limit': 1})
471         [record] = models.execute_kw(db, uid, password,
472             'res.partner', 'read', [ids])
473         # count the number of fields fetched by default
474         len(record)
475
476     .. code-block:: ruby
477
478         ids = models.execute_kw(db, uid, password,
479             'res.partner', 'search',
480             [[['is_company', '=', true], ['customer', '=', true]]],
481             {limit: 1})
482         record = models.execute_kw(db, uid, password,
483             'res.partner', 'read', [ids]).first
484         # count the number of fields fetched by default
485         record.length
486
487     .. code-block:: php
488
489         $ids = $models->execute_kw($db, $uid, $password,
490             'res.partner', 'search',
491             array($domain),
492             array('limit'=>1));
493         $records = $models->execute_kw($db, $uid, $password,
494             'res.partner', 'read', array($ids));
495         // count the number of fields fetched by default
496         count($records[0]);
497
498     .. code-block:: java
499
500         final List ids = Arrays.asList((Object[])models.execute(
501             "execute_kw", Arrays.asList(
502                 db, uid, password,
503                 "res.partner", "search",
504                 Arrays.asList(domain),
505                 new HashMap() {{ put("limit", 1); }})));
506         final Map record = (Map)((Object[])models.execute(
507             "execute_kw", Arrays.asList(
508                 db, uid, password,
509                 "res.partner", "read",
510                 Arrays.asList(ids)
511             )
512         ))[0];
513         // count the number of fields fetched by default
514         record.size();
515
516 .. code-block:: json
517
518     121
519
520 Conversedly, picking only three fields deemed interesting.
521
522 .. rst-class:: switchable
523
524     .. code-block:: python
525
526         models.execute_kw(db, uid, password,
527             'res.partner', 'read',
528             [ids], {'fields': ['name', 'country_id', 'comment']})
529
530     .. code-block:: ruby
531
532         models.execute_kw(db, uid, password,
533             'res.partner', 'read',
534             [ids], {fields: %w(name country_id comment)})
535
536     .. code-block:: php
537
538         $models->execute_kw($db, $uid, $password,
539             'res.partner', 'read',
540             array($ids),
541             array('fields'=>array('name', 'country_id', 'comment')));
542
543     .. code-block:: java
544
545         Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
546             db, uid, password,
547             "res.partner", "read",
548             Arrays.asList(ids),
549             new HashMap() {{
550                 put("fields", Arrays.asList("name", "country_id", "comment"));
551             }}
552         )));
553
554 .. code-block:: json
555
556     [{"comment": false, "country_id": [21, "Belgium"], "id": 7, "name": "Agrolait"}]
557
558 .. note:: even if the ``id`` field is not requested, it is always returned
559
560 Listing record fields
561 ---------------------
562
563 :meth:`~openerp.models.Model.fields_get` can be used to inspect
564 a model's fields and check which ones seem to be of interest.
565
566 Because
567 it returns a great amount of meta-information (it is also used by client
568 programs) it should be filtered before printing, the most interesting items
569 for a human user are ``string`` (the field's label), ``help`` (a help text if
570 available) and ``type`` (to know which values to expect, or to send when
571 updating a record):
572
573 .. rst-class:: switchable
574
575     .. code-block:: python
576
577         fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
578         # filter keys of field attributes for display
579         {field: {
580                     k: v for k, v in attributes.iteritems()
581                     if k in ['string', 'help', 'type']
582                 }
583          for field, attributes in fields.iteritems()}
584
585     .. code-block:: ruby
586
587         fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
588         # filter keys of field attributes for display
589         fields.each {|k, v|
590             fields[k] = v.keep_if {|kk, vv| %w(string help type).include? kk}
591         }
592
593     .. code-block:: php
594
595         $fields_full = $models->execute_kw($db, $uid, $password,
596             'res.partner', 'fields_get', array());
597         // filter keys of field attributes for display
598         $allowed = array_flip(array('string', 'help', 'type'));
599         $fields = array();
600         foreach($fields_full as $field => $attributes) {
601           $fields[$field] = array_intersect_key($attributes, $allowed);
602         }
603
604     .. code-block:: java
605
606         final Map<String, Map<String, Object>> fields  =
607             (Map<String, Map<String, Object>>)models.execute("execute_kw", Arrays.asList(
608                 db, uid, password,
609                 "res.partner", "fields_get",
610                 Collections.emptyList()));
611         // filter keys of field attributes for display
612         final List<String> allowed = Arrays.asList("string", "help", "type");
613         new HashMap<String, Map<String, Object>>() {{
614             for(Entry<String, Map<String, Object>> item: fields.entrySet()) {
615                 put(item.getKey(), new HashMap<String, Object>() {{
616                     for(Entry<String, Object> it: item.getValue().entrySet()) {
617                         if (allowed.contains(it.getKey())) {
618                             put(it.getKey(), it.getValue());
619                         }
620                     }
621                 }});
622             }
623         }};
624
625 .. code-block:: json
626
627     {
628         "ean13": {
629             "type": "char",
630             "help": "BarCode",
631             "string": "EAN13"
632         },
633         "property_account_position": {
634             "type": "many2one",
635             "help": "The fiscal position will determine taxes and accounts used for the partner.",
636             "string": "Fiscal Position"
637         },
638         "signup_valid": {
639             "type": "boolean",
640             "help": "",
641             "string": "Signup Token is Valid"
642         },
643         "date_localization": {
644             "type": "date",
645             "help": "",
646             "string": "Geo Localization Date"
647         },
648         "ref_companies": {
649             "type": "one2many",
650             "help": "",
651             "string": "Companies that refers to partner"
652         },
653         "sale_order_count": {
654             "type": "integer",
655             "help": "",
656             "string": "# of Sales Order"
657         },
658         "purchase_order_count": {
659             "type": "integer",
660             "help": "",
661             "string": "# of Purchase Order"
662         },
663
664 Search and read
665 ---------------
666
667 Because that is a very common task, Odoo provides a
668 :meth:`~openerp.models.Model.search_read` shortcut which as its name notes is
669 equivalent to a :meth:`~openerp.models.Model.search` followed by a
670 :meth:`~openerp.models.Model.read`, but avoids having to perform two requests
671 and keep ids around. Its arguments are similar to
672 :meth:`~openerp.models.Model.search`'s, but it can also take a list of
673 ``fields`` (like :meth:`~openerp.models.Model.read`, if that list is not
674 provided it'll fetch all fields of matched records):
675
676 .. rst-class:: switchable
677
678     .. code-block:: python
679
680         models.execute_kw(db, uid, password,
681             'res.partner', 'search_read',
682             [[['is_company', '=', True], ['customer', '=', True]]],
683             {'fields': ['name', 'country_id', 'comment'], 'limit': 5})
684
685     .. code-block:: ruby
686
687         models.execute_kw(db, uid, password,
688             'res.partner', 'search_read',
689             [[['is_company', '=', true], ['customer', '=', true]]],
690             {fields: %w(name country_id comment), limit: 5})
691
692     .. code-block:: php
693
694         $models->execute_kw($db, $uid, $password,
695             'res.partner', 'search_read',
696             array($domain),
697             array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
698
699     .. code-block:: java
700
701         Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
702             db, uid, password,
703             "res.partner", "search_read",
704             Arrays.asList(domain),
705             new HashMap() {{
706                 put("fields", Arrays.asList("name", "country_id", "comment"));
707                 put("limit", 5);
708             }}
709         )));
710
711 .. code-block:: json
712
713     [
714         {
715             "comment": false,
716             "country_id": [ 21, "Belgium" ],
717             "id": 7,
718             "name": "Agrolait"
719         },
720         {
721             "comment": false,
722             "country_id": [ 76, "France" ],
723             "id": 18,
724             "name": "Axelor"
725         },
726         {
727             "comment": false,
728             "country_id": [ 233, "United Kingdom" ],
729             "id": 12,
730             "name": "Bank Wealthy and sons"
731         },
732         {
733             "comment": false,
734             "country_id": [ 105, "India" ],
735             "id": 14,
736             "name": "Best Designers"
737         },
738         {
739             "comment": false,
740             "country_id": [ 76, "France" ],
741             "id": 17,
742             "name": "Camptocamp"
743         }
744     ]
745
746
747 Create records
748 --------------
749
750 .. rst-class:: switchable
751
752     .. code-block:: python
753
754         id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
755             'name': "New Partner",
756         }])
757
758     .. code-block:: ruby
759
760         id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
761             name: "New Partner",
762         }])
763
764     .. code-block:: php
765
766         $id = $models->execute_kw($db, $uid, $password,
767             'res.partner', 'create',
768             array(array('name'=>"New Partner")));
769
770     .. code-block:: java
771
772         final Integer id = (Integer)models.execute("execute_kw", Arrays.asList(
773             db, uid, password,
774             "res.partner", "create",
775             Arrays.asList(new HashMap() {{ put("name", "New Partner"); }})
776         ));
777
778 .. code-block:: json
779
780     78
781
782 Update records
783 --------------
784
785 .. rst-class:: switchable
786
787     .. code-block:: python
788
789         models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
790             'name': "Newer partner"
791         }])
792         # get record name after having changed it
793         models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
794
795     .. code-block:: ruby
796
797         models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
798             name: "Newer partner"
799         }])
800         # get record name after having changed it
801         models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
802
803     .. code-block:: php
804
805         $models->execute_kw($db, $uid, $password, 'res.partner', 'write',
806             array(array($id), array('name'=>"Newer partner")));
807         // get record name after having changed it
808         $models->execute_kw($db, $uid, $password,
809             'res.partner', 'name_get', array(array($id)));
810
811     .. code-block:: java
812
813         models.execute("execute_kw", Arrays.asList(
814             db, uid, password,
815             "res.partner", "write",
816             Arrays.asList(
817                 Arrays.asList(id),
818                 new HashMap() {{ put("name", "Newer Partner"); }}
819             )
820         ));
821         // get record name after having changed it
822         Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
823             db, uid, password,
824             "res.partner", "name_get",
825             Arrays.asList(Arrays.asList(id))
826         )));
827
828 .. code-block:: json
829
830     [[78, "Newer partner"]]
831
832 Delete records
833 --------------
834
835 .. rst-class:: switchable
836
837     .. code-block:: python
838
839         models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
840         # check if the deleted record is still in the database
841         models.execute_kw(db, uid, password,
842             'res.partner', 'search', [[['id', '=', id]]])
843
844     .. code-block:: ruby
845
846         models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
847         # check if the deleted record is still in the database
848         models.execute_kw(db, uid, password,
849             'res.partner', 'search', [[['id', '=', id]]])
850
851     .. code-block:: php
852
853         $models->execute_kw($db, $uid, $password,
854             'res.partner', 'unlink',
855             array(array($id)));
856         // check if the deleted record is still in the database
857         $models->execute_kw($db, $uid, $password,
858             'res.partner', 'search',
859             array(array(array('id', '=', $id))));
860
861     .. code-block:: java
862
863         models.execute("execute_kw", Arrays.asList(
864             db, uid, password,
865             "res.partner", "unlink",
866             Arrays.asList(Arrays.asList(id))));
867         // check if the deleted record is still in the database
868         Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
869             db, uid, password,
870             "res.partner", "search",
871             Arrays.asList(Arrays.asList(Arrays.asList("id", "=", 78)))
872         )));
873
874 .. code-block:: json
875
876     []
877
878 .. _PostgreSQL: http://www.postgresql.org
879 .. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC