[ADD] Stripe-style APIDoc: content

This commit is contained in:
Xavier Morel 2014-10-20 07:47:14 +02:00
parent eb8aad865e
commit 4d2b24adcb
9 changed files with 970 additions and 62 deletions

View File

@ -25,3 +25,8 @@ class Exercise(admonitions.BaseAdmonition):
from sphinx.locale import admonitionlabels, l_
admonitionlabels['exercise'] = l_('Exercise')
# monkeypatch PHP lexer to not require <?php
from sphinx.highlighting import lexers
from pygments.lexers.web import PhpLexer
lexers['php'] = PhpLexer(startinline=True)

View File

@ -21,7 +21,7 @@
{%- endblock -%}
{%- block content -%}
<div class="document-super">
<div class="document-super {% if meta is defined %}{{ meta.classes }}{% endif %}">
{{ super() }}
</div>
{%- endblock -%}

View File

@ -6174,7 +6174,11 @@ button.close {
display: none !important;
}
}
* {
box-sizing: border-box;
}
body {
overflow: auto;
position: relative;
}
.document-super {
@ -6643,20 +6647,11 @@ div.section > h2 {
*
* Generated via Pygments
*/
.highlight {
padding: 9px 14px;
margin-bottom: 14px;
background-color: #f7f7f9 !important;
border: 1px solid #e1e1e8;
border-radius: 4px;
}
.highlight pre {
color: #333;
padding: 0 45px 0 0;
margin-top: 0;
margin-bottom: 0;
background-color: transparent;
border: 0;
padding: 4px;
font-size: 75%;
word-break: normal;
word-wrap: normal;
}
/*
* ZeroClipboard styles
@ -6665,6 +6660,11 @@ div.section > h2 {
position: relative;
display: none;
}
@media (min-width: 768px) {
.zero-clipboard {
display: block;
}
}
.btn-clipboard {
position: absolute;
top: 0;
@ -6684,11 +6684,6 @@ div.section > h2 {
background-color: #a24689;
border-color: #a24689;
}
@media (min-width: 768px) {
.zero-clipboard {
display: block;
}
}
img.align-center {
display: block;
margin: 0 auto;
@ -6710,10 +6705,63 @@ td.field-body > ul {
margin: 0;
padding: 0;
}
pre {
word-break: normal;
word-wrap: normal;
}
.descclassname {
opacity: 0.5;
}
.stripe .section {
margin-bottom: 2em;
}
@media (min-width: 992px) {
.stripe .section > *,
.stripe .section > .force-left {
width: 49%;
float: left;
clear: left;
}
.stripe .section > .force-right,
.stripe .section > [class*=highlight] {
float: none;
clear: none;
margin-left: 51%;
}
.stripe .section > h1,
.stripe .section > h2,
.stripe .section > h3,
.stripe .section > h4,
.stripe .section > h5,
.stripe .section > h6 {
background-color: rgba(255, 255, 255, 0.7);
}
.stripe .section > h1,
.stripe .section > h2,
.stripe .section > h3,
.stripe .section > h4,
.stripe .section > h5,
.stripe .section > h6,
.stripe .section > .section {
position: relative;
width: auto;
float: none;
clear: both;
}
.stripe .bodywrapper {
position: relative;
}
.stripe .bodywrapper:before {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
content: "";
width: 0;
border-left: 1px solid #777777;
}
}
.stripe .switchable > .highlight,
.stripe [class*=only-] {
display: none;
}
.stripe .only-python,
.stripe .highlight-python > .highlight {
display: block;
}

View File

@ -17,7 +17,11 @@
// indent level for various items list e.g. dl, fields lists, ...
@item-indent: 30px;
* {
box-sizing: border-box;
}
body {
overflow: auto;
position: relative;
}
@ -461,21 +465,13 @@ div.section > h2 {
*
* Generated via Pygments
*/
.highlight {
padding: 9px 14px;
margin-bottom: 14px;
background-color: #f7f7f9 !important;
border: 1px solid #e1e1e8;
border-radius: 4px;
}
.highlight pre {
color: #333;
padding: 0 45px 0 0;
margin-top: 0;
margin-bottom: 0;
background-color: transparent;
border: 0;
padding: 4px;
font-size: 75%;
// code block lines should not wrap
word-break: normal;
word-wrap: normal;
}
/*
@ -485,6 +481,9 @@ div.section > h2 {
.zero-clipboard {
position: relative;
display: none;
@media (min-width: @screen-sm-min) {
display: block;
}
}
.btn-clipboard {
position: absolute;
@ -506,12 +505,6 @@ div.section > h2 {
border-color: @brand-primary;
}
@media (min-width: 768px) {
.zero-clipboard {
display: block;
}
}
// rST styles
img.align-center {
display: block;
@ -546,13 +539,67 @@ td.field-body {
padding: 0;
}
// code block lines should not wrap
pre {
word-break: normal;
word-wrap: normal;
}
// lighten js namespace/class name
.descclassname {
opacity: 0.5;
}
// STRIPE-STYLE PAGES
.stripe {
.section {
margin-bottom: 2em;
}
// === columning only on medium+ ===
@media (min-width: @screen-md-min) {
// column 1
.section > *,
.section > .force-left {
width: 49%;
float: left;
clear: left;
}
// column 2
.section > .force-right,
.section > [class*=highlight] {
float: none;
clear: none;
margin-left: 51%;
}
// fullwidth elements
.section > h1, .section > h2, .section > h3, .section > h4, .section > h5,
.section > h6 {
background-color: fadeout(@body-bg, 30%);
}
.section > h1, .section > h2, .section > h3, .section > h4, .section > h5,
.section > h6, .section > .section {
position: relative;
width: auto;
float: none;
clear: both;
}
.bodywrapper {
position: relative;
// middle separator
&:before {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
content: "";
width: 0;
border-left: 1px solid @gray-light;
}
}
}
// === show/hide code snippets ===
.switchable > .highlight,
[class*=only-] {
display: none;
}
// must be final rule of page
.only-python, .highlight-python > .highlight {
display: block;
}
}

View File

@ -5,3 +5,4 @@ Module Objects
.. toctree::
:titlesonly:
modules/api_integration

View File

@ -0,0 +1,816 @@
:classes: stripe
===========
Odoo as API
===========
Odoo is mostly extended internally via modules, but much of its features and
all of its data is also available from the outside for external analysis or
integration with various tools. Part of the :ref:`reference/orm/model` API is
easily available over XML-RPC_ and accessible from a variety of languages.
.. Odoo XML-RPC idiosyncracies:
* uses multiple endpoint and a nested call syntax instead of a
"hierarchical" server structure (e.g. ``openerp.res.partner.read()``)
* uses its own own manual auth system instead of basic auth or sessions
(basic is directly supported the Python and Ruby stdlibs as well as
ws-xmlrpc, not sure about ripcord)
* own auth is inconvenient as (uid, password) have to be explicitly passed
into every call. Session would allow db to be stored as well
These issues are especially visible in Java, somewhat less so in PHP
Connection and authentication
=============================
Configuration
-------------
If you already have an Odoo server installed, you can just use its
parameters
.. rst-class:: switchable
.. code-block:: python
url = <insert server URL>
db = <insert database name>
username = 'admin'
password = <insert password for your admin user (default: admin)>
.. code-block:: ruby
url = <insert server URL>
db = <insert database name>
username = "admin"
password = <insert password for your admin user (default: admin)>
.. code-block:: php
$url = <insert server URL>;
$db = <insert database name>;
$username = "admin";
$password = <insert password for your admin user (default: admin)>;
.. code-block:: java
final String url = <insert server URL>,
db = <insert database name>,
username = "admin",
password = <insert password for your admin user (default: admin)>;
To make exploration simpler, you can also ask https://demo.odoo.com for a test
database:
.. rst-class:: switchable
.. code-block:: python
import xmlrpclib
info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
url, db, username, password = \
info['host'], info['database'], info['user'], info['password']
.. code-block:: ruby
require "xmlrpc/client"
info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
url, db, username, password = \
info['host'], info['database'], info['user'], info['password']
.. code-block:: php
require_once('ripcord.php');
$info = ripcord::client('https://demo.odoo.com/start')->start();
list($url, $db, $username, $password) =
array($info['host'], $info['database'], $info['user'], $info['password']);
.. code-block:: java
final XmlRpcClient client = new XmlRpcClient();
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
final Map<String, String> info = (Map<String, String>)client.execute(
start_config, "start", Collections.emptyList());
final String url = info.get("host"),
db = info.get("database"),
username = info.get("user"),
password = info.get("password");
.. rst-class:: force-right
.. note::
:class: only-php
These examples use the `Ripcord <https://code.google.com/p/ripcord/>`_
library, which provides a simple XML-RPC API. Ripcord requires that
`XML-RPC support be enabled
<http://php.net/manual/en/xmlrpc.installation.php>`_ in your PHP
installation.
Since calls are performed over
`HTTPS <http://en.wikipedia.org/wiki/HTTP_Secure>`_, it also requires that
the `OpenSSL extension
<http://php.net/manual/en/openssl.installation.php>`_ be enabled.
.. note::
:class: only-java
These examples use the `Apache XML-RPC library
<https://ws.apache.org/xmlrpc/>`_
Logging in
----------
Odoo requires users of the API to be authenticated before being able to query
much data.
The ``xmlrpc/2/common`` endpoint provides meta-calls which don't require
authentication, such as the authentication itself or fetching version
information. To verify if the connection information is correct before trying
to authenticate, the simplest call is to ask for the server's version. The
authentication itself is done through the ``authenticate`` function and
returns a user identifier (``uid``) used in authenticated calls instead of
the login.
.. rst-class:: switchable
.. code-block:: python
common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
common.version()
.. code-block:: ruby
common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
common.call('version')
.. code-block:: php
$common = ripcord::client("$url/xmlrpc/2/common");
$common->version();
.. code-block:: java
final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
client.execute(common_config, "version", Collections.emptyList());
.. code-block:: json
{
"server_version": "8.0",
"server_version_info": [8, 0, 0, "final", 0],
"server_serie": "8.0",
"protocol_version": 1,
}
.. rst-class:: switchable
.. code-block:: python
uid = common.authenticate(db, username, password, {})
.. code-block:: ruby
uid = common.call('authenticate', db, username, password, {})
.. code-block:: php
$uid = $common->authenticate($db, $username, $password, array());
.. code-block:: java
int uid = (int)client.execute(
common_config, "authenticate", Arrays.asList(
db, username, password, Collections.emptyMap()));
Calling methods
===============
The second — and most generally useful — is ``xmlrpc/2/object`` which is used
to call methods of odoo models via the ``execute_kw`` RPC function.
Each call to ``execute_kw`` takes the following parameters:
* the database to use, a string
* the user id (retrieved through ``authenticate``), an integer
* the user's password, a string
* the model name, a string
* the method name, a string
* an array/list of parameters passed by position
* a mapping/dict of parameters to pass by keyword (optional)
.. rst-class:: force-right
For instance to see if we can read the ``res.partner`` model we can call
``check_access_rights`` with ``operation`` passed by position and
``raise_exception`` passed by keyword (in order to get a true/false result
rather than true/error):
.. rst-class:: switchable
.. code-block:: python
models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
models.execute_kw(db, uid, password,
'res.partner', 'check_access_rights',
['read'], {'raise_exception': False})
.. code-block:: ruby
models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
models.execute_kw(db, uid, password,
'res.partner', 'check_access_rights',
['read'], {raise_exception: false})
.. code-block:: php
$models = ripcord::client("$url/xmlrpc/2/object");
$models->execute_kw($db, $uid, $password,
'res.partner', 'check_access_rights',
array('read'), array('raise_exception' => false));
.. code-block:: java
final XmlRpcClient models = new XmlRpcClient() {{
setConfig(new XmlRpcClientConfigImpl() {{
setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
}});
}};
models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "check_access_rights",
Arrays.asList("read"),
new HashMap() {{ put("raise_exception", false); }}
));
.. code-block:: json
true
.. todo:: this should be runnable and checked
List records
------------
Records can be listed and filtered via :meth:`~openerp.models.Model.search`.
:meth:`~openerp.models.Model.search` takes a mandatory
:ref:`domain <reference/orm/domains>` filter (possibly empty), and returns the
database identifiers of all records matching the filter. To list customer
companies for instance:
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', True], ['customer', '=', True]]])
.. code-block:: ruby
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', true], ['customer', '=', true]]])
.. code-block:: php
$domain = array(array('is_company', '=', true),
array('customer', '=', true));
$models->execute_kw($db, $uid, $password,
'res.partner', 'search', array($domain));
.. code-block:: java
final List domain = Arrays.asList(
Arrays.asList("is_company", "=", true),
Arrays.asList("customer", "=", true));
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "search",
Arrays.asList(domain)
)));
.. code-block:: json
[7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74]
Pagination
''''''''''
By default a research will return the ids of all records matching the
condition, which may be a huge number. ``offset`` and ``limit`` parameters are
available to only retrieve a subset of all matched records.
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', True], ['customer', '=', True]]],
{'offset': 10, 'limit': 5})
.. code-block:: ruby
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', true], ['customer', '=', true]]],
{offset: 10, limit: 5})
.. code-block:: php
$models->execute_kw($db, $uid, $password,
'res.partner', 'search',
array($domain),
array('offset'=>10, 'limit'=>5));
.. code-block:: java
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "search",
Arrays.asList(domain),
new HashMap() {{ put("offset", 10); put("limit", 5); }}
)));
.. code-block:: json
[13, 20, 30, 22, 29]
Count records
-------------
Rather than retrieve a possibly gigantic list of records and count them
afterwards, :meth:`~openerp.models.Model.search_count` can be used to retrieve
only the number of records matching the query. It takes the same
:ref:`domain <reference/orm/domains>` filter as
:meth:`~openerp.models.Model.search` and no other parameter.
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password,
'res.partner', 'search_count',
[[['is_company', '=', True], ['customer', '=', True]]])
.. code-block:: ruby
models.execute_kw(db, uid, password,
'res.partner', 'search_count',
[[['is_company', '=', true], ['customer', '=', true]]])
.. code-block:: php
$models->execute_kw($db, $uid, $password,
'res.partner', 'search_count',
array($domain));
.. code-block:: java
(Integer)models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "search_count",
Arrays.asList(domain)
));
.. code-block:: json
19
.. warning::
calling ``search`` then ``search_count`` (or the other way around) may not
yield coherent results if other users are using the server: stored data
could have changed between the calls
Read records
------------
Record data is accessible via the :meth:`~openerp.models.Model.read` method,
which takes a list of ids (as returned by
:meth:`~openerp.models.Model.search`) and optionally a list of fields to
fetch. By default, it will fetch all the fields the current user can read,
which tends to be a huge amount.
.. rst-class:: switchable
.. code-block:: python
ids = models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', True], ['customer', '=', True]]],
{'limit': 1})
[record] = models.execute_kw(db, uid, password,
'res.partner', 'read', [ids])
# count the number of fields fetched by default
len(record)
.. code-block:: ruby
ids = models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', true], ['customer', '=', true]]],
{limit: 1})
record = models.execute_kw(db, uid, password,
'res.partner', 'read', [ids]).first
# count the number of fields fetched by default
record.length
.. code-block:: php
$ids = $models->execute_kw($db, $uid, $password,
'res.partner', 'search',
array($domain),
array('limit'=>1));
$records = $models->execute_kw($db, $uid, $password,
'res.partner', 'read', array($ids));
// count the number of fields fetched by default
count($records[0]);
.. code-block:: java
final List ids = Arrays.asList((Object[])models.execute(
"execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "search",
Arrays.asList(domain),
new HashMap() {{ put("limit", 1); }})));
final Map record = (Map)((Object[])models.execute(
"execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "read",
Arrays.asList(ids)
)
))[0];
// count the number of fields fetched by default
record.size();
.. code-block:: json
121
Conversedly, picking only three fields deemed interesting.
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password,
'res.partner', 'read',
[ids], {'fields': ['name', 'country_id', 'comment']})
.. code-block:: ruby
models.execute_kw(db, uid, password,
'res.partner', 'read',
[ids], {fields: %w(name country_id comment)})
.. code-block:: php
$models->execute_kw($db, $uid, $password,
'res.partner', 'read',
array($ids),
array('fields'=>array('name', 'country_id', 'comment')));
.. code-block:: java
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "read",
Arrays.asList(ids),
new HashMap() {{
put("fields", Arrays.asList("name", "country_id", "comment"));
}}
)));
.. code-block:: json
[{"comment": false, "country_id": [21, "Belgium"], "id": 7, "name": "Agrolait"}]
.. note:: even if the ``id`` field is not requested, it is always returned
Listing record fields
---------------------
:meth:`~openerp.models.Model.fields_get` can be used to inspect
a model's fields and check which ones seem to be of interest.
Because
it returns a great amount of meta-information (it is also used by client
programs) it should be filtered before printing, the most interesting items
for a human user are ``string`` (the field's label), ``help`` (a help text if
available) and ``type`` (to know which values to expect, or to send when
updating a record):
.. rst-class:: switchable
.. code-block:: python
fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
# filter keys of field attributes for display
{field: {
k: v for k, v in attributes.iteritems()
if k in ['string', 'help', 'type']
}
for field, attributes in fields.iteritems()}
.. code-block:: ruby
fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
# filter keys of field attributes for display
fields.each {|k, v|
fields[k] = v.keep_if {|kk, vv| %w(string help type).include? kk}
}
.. code-block:: php
$fields_full = $models->execute_kw($db, $uid, $password,
'res.partner', 'fields_get', array());
// filter keys of field attributes for display
$allowed = array_flip(array('string', 'help', 'type'));
$fields = array();
foreach($fields_full as $field => $attributes) {
$fields[$field] = array_intersect_key($attributes, $allowed);
}
.. code-block:: java
final Map<String, Map<String, Object>> fields =
(Map<String, Map<String, Object>>)models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "fields_get",
Collections.emptyList()));
// filter keys of field attributes for display
final List<String> allowed = Arrays.asList("string", "help", "type");
new HashMap<String, Map<String, Object>>() {{
for(Entry<String, Map<String, Object>> item: fields.entrySet()) {
put(item.getKey(), new HashMap<String, Object>() {{
for(Entry<String, Object> it: item.getValue().entrySet()) {
if (allowed.contains(it.getKey())) {
put(it.getKey(), it.getValue());
}
}
}});
}
}};
.. code-block:: json
{
"ean13": {
"type": "char",
"help": "BarCode",
"string": "EAN13"
},
"property_account_position": {
"type": "many2one",
"help": "The fiscal position will determine taxes and accounts used for the partner.",
"string": "Fiscal Position"
},
"signup_valid": {
"type": "boolean",
"help": "",
"string": "Signup Token is Valid"
},
"date_localization": {
"type": "date",
"help": "",
"string": "Geo Localization Date"
},
"ref_companies": {
"type": "one2many",
"help": "",
"string": "Companies that refers to partner"
},
"sale_order_count": {
"type": "integer",
"help": "",
"string": "# of Sales Order"
},
"purchase_order_count": {
"type": "integer",
"help": "",
"string": "# of Purchase Order"
},
Search and read
---------------
Because that is a very common task, Odoo provides a
:meth:`~openerp.models.Model.search_read` shortcut which as its name notes is
equivalent to a :meth:`~openerp.models.Model.search` followed by a
:meth:`~openerp.models.Model.read`, but avoids having to perform two requests
and keep ids around. Its arguments are similar to
:meth:`~openerp.models.Model.search`'s, but it can also take a list of
``fields`` (like :meth:`~openerp.models.Model.read`, if that list is not
provided it'll fetch all fields of matched records):
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password,
'res.partner', 'search_read',
[[['is_company', '=', True], ['customer', '=', True]]],
{'fields': ['name', 'country_id', 'comment'], 'limit': 5})
.. code-block:: ruby
models.execute_kw(db, uid, password,
'res.partner', 'search_read',
[[['is_company', '=', true], ['customer', '=', true]]],
{fields: %w(name country_id comment), limit: 5})
.. code-block:: php
$models->execute_kw($db, $uid, $password,
'res.partner', 'search_read',
array($domain),
array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
.. code-block:: java
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "search_read",
Arrays.asList(domain),
new HashMap() {{
put("fields", Arrays.asList("name", "country_id", "comment"));
put("limit", 5);
}}
)));
.. code-block:: json
[
{
"comment": false,
"country_id": [ 21, "Belgium" ],
"id": 7,
"name": "Agrolait"
},
{
"comment": false,
"country_id": [ 76, "France" ],
"id": 18,
"name": "Axelor"
},
{
"comment": false,
"country_id": [ 233, "United Kingdom" ],
"id": 12,
"name": "Bank Wealthy and sons"
},
{
"comment": false,
"country_id": [ 105, "India" ],
"id": 14,
"name": "Best Designers"
},
{
"comment": false,
"country_id": [ 76, "France" ],
"id": 17,
"name": "Camptocamp"
}
]
Create records
--------------
.. rst-class:: switchable
.. code-block:: python
id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
'name': "New Partner",
}])
.. code-block:: ruby
id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
name: "New Partner",
}])
.. code-block:: php
$id = $models->execute_kw($db, $uid, $password,
'res.partner', 'create',
array(array('name'=>"New Partner")));
.. code-block:: java
final Integer id = (Integer)models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "create",
Arrays.asList(new HashMap() {{ put("name", "New Partner"); }})
));
.. code-block:: json
78
Update records
--------------
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
'name': "Newer partner"
}])
# get record name after having changed it
models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
.. code-block:: ruby
models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
name: "Newer partner"
}])
# get record name after having changed it
models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
.. code-block:: php
$models->execute_kw($db, $uid, $password, 'res.partner', 'write',
array(array($id), array('name'=>"Newer partner")));
// get record name after having changed it
$models->execute_kw($db, $uid, $password,
'res.partner', 'name_get', array(array($id)));
.. code-block:: java
models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "write",
Arrays.asList(
Arrays.asList(id),
new HashMap() {{ put("name", "Newer Partner"); }}
)
));
// get record name after having changed it
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "name_get",
Arrays.asList(Arrays.asList(id))
)));
.. code-block:: json
[[78, "Newer partner"]]
Delete records
--------------
.. rst-class:: switchable
.. code-block:: python
models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
# check if the deleted record is still in the database
models.execute_kw(db, uid, password,
'res.partner', 'search', [[['id', '=', id]]])
.. code-block:: ruby
models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
# check if the deleted record is still in the database
models.execute_kw(db, uid, password,
'res.partner', 'search', [[['id', '=', id]]])
.. code-block:: php
$models->execute_kw($db, $uid, $password,
'res.partner', 'unlink',
array(array($id)));
// check if the deleted record is still in the database
$models->execute_kw($db, $uid, $password,
'res.partner', 'search',
array(array(array('id', '=', $id))));
.. code-block:: java
models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "unlink",
Arrays.asList(Arrays.asList(id))));
// check if the deleted record is still in the database
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
db, uid, password,
"res.partner", "search",
Arrays.asList(Arrays.asList(Arrays.asList("id", "=", 78)))
)));
.. code-block:: json
[]
.. _PostgreSQL: http://www.postgresql.org
.. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC

View File

@ -781,7 +781,7 @@ class ir_model_access(osv.osv):
_logger.warning('Access Denied by ACLs for operation: %s, uid: %s, model: %s', mode, uid, model_name)
msg = '%s %s' % (msg_heads[mode], msg_tail)
raise openerp.exceptions.AccessError(msg % msg_params)
return r or False
return bool(r)
__cache_clearing_methods = []

View File

@ -1654,7 +1654,7 @@ class BaseModel(object):
@api.returns('self')
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
""" search(args[, offset=0][, limit=None][, order=None][, count=False])
""" search(args[, offset=0][, limit=None][, order=None])
Searches for records based on the ``args``
:ref:`search domain <reference/orm/domains>`.
@ -1664,9 +1664,6 @@ class BaseModel(object):
:param int offset: number of results to ignore (default: none)
:param int limit: maximum number of records to return (default: all)
:param str order: sort string
:param bool count: if ``True``, the call should return the number of
records matching ``args`` rather than the records
themselves.
:returns: at most ``limit`` records matching the search criteria
:raise AccessError: * if user tries to bypass access rules for read on the requested object.

View File

@ -18,14 +18,8 @@ RPC_VERSION_1 = {
}
def dispatch(method, params):
if method in ['login', 'about', 'timezone_get',
'version', 'authenticate']:
pass
elif method in ['set_loglevel']:
passwd = params[0]
params = params[1:]
security.check_super(passwd)
else:
if method not in ['login', 'about', 'timezone_get',
'version', 'authenticate', 'set_loglevel']:
raise Exception("Method not found: %s" % method)
fn = globals()['exp_' + method]