[ADD] ws doc: introspection, reports and workflows
* use static imports in java examples to make them terser * inline ``domain`` in java and php example to make examples more self-contained * try to extend/improve Model.write's docstring * add convenience kwarg to fields_get, mostly for user-driven introspection Closes #3689
This commit is contained in:
parent
467968b79a
commit
ec7736a051
|
@ -61,11 +61,10 @@ Connection and authentication
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
final XmlRpcClient client = new XmlRpcClient();
|
final XmlRpcClient client = new XmlRpcClient();
|
||||||
|
|
||||||
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
|
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
|
||||||
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
|
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
|
||||||
final Map<String, String> info = (Map<String, String>)client.execute(
|
final Map<String, String> info = (Map<String, String>)client.execute(
|
||||||
start_config, "start", Collections.emptyList());
|
start_config, "start", emptyList());
|
||||||
|
|
||||||
final String url = info.get("host"),
|
final String url = info.get("host"),
|
||||||
db = info.get("database"),
|
db = info.get("database"),
|
||||||
|
@ -77,7 +76,7 @@ Connection and authentication
|
||||||
|
|
||||||
int uid = (int)client.execute(
|
int uid = (int)client.execute(
|
||||||
common_config, "authenticate", Arrays.asList(
|
common_config, "authenticate", Arrays.asList(
|
||||||
db, username, password, Collections.emptyMap()));
|
db, username, password, emptyMap()));
|
||||||
|
|
||||||
final XmlRpcClient models = new XmlRpcClient() {{
|
final XmlRpcClient models = new XmlRpcClient() {{
|
||||||
setConfig(new XmlRpcClientConfigImpl() {{
|
setConfig(new XmlRpcClientConfigImpl() {{
|
||||||
|
@ -154,7 +153,7 @@ database:
|
||||||
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
|
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
|
||||||
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
|
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
|
||||||
final Map<String, String> info = (Map<String, String>)client.execute(
|
final Map<String, String> info = (Map<String, String>)client.execute(
|
||||||
start_config, "start", Collections.emptyList());
|
start_config, "start", emptyList());
|
||||||
|
|
||||||
final String url = info.get("host"),
|
final String url = info.get("host"),
|
||||||
db = info.get("database"),
|
db = info.get("database"),
|
||||||
|
@ -183,6 +182,9 @@ database:
|
||||||
These examples use the `Apache XML-RPC library
|
These examples use the `Apache XML-RPC library
|
||||||
<https://ws.apache.org/xmlrpc/>`_
|
<https://ws.apache.org/xmlrpc/>`_
|
||||||
|
|
||||||
|
The examples do not include imports as these imports couldn't be
|
||||||
|
pasted in the code.
|
||||||
|
|
||||||
Logging in
|
Logging in
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -217,8 +219,9 @@ the login.
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
|
final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
|
||||||
common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
|
common_config.setServerURL(
|
||||||
client.execute(common_config, "version", Collections.emptyList());
|
new URL(String.format("%s/xmlrpc/2/common", url)));
|
||||||
|
client.execute(common_config, "version", emptyList());
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
|
@ -246,8 +249,8 @@ the login.
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
int uid = (int)client.execute(
|
int uid = (int)client.execute(
|
||||||
common_config, "authenticate", Arrays.asList(
|
common_config, "authenticate", asList(
|
||||||
db, username, password, Collections.emptyMap()));
|
db, username, password, emptyMap()));
|
||||||
|
|
||||||
Calling methods
|
Calling methods
|
||||||
===============
|
===============
|
||||||
|
@ -302,10 +305,10 @@ rather than true/error):
|
||||||
setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
|
setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
|
||||||
}});
|
}});
|
||||||
}};
|
}};
|
||||||
models.execute("execute_kw", Arrays.asList(
|
models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "check_access_rights",
|
"res.partner", "check_access_rights",
|
||||||
Arrays.asList("read"),
|
asList("read"),
|
||||||
new HashMap() {{ put("raise_exception", false); }}
|
new HashMap() {{ put("raise_exception", false); }}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -341,20 +344,19 @@ companies for instance:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$domain = array(array('is_company', '=', true),
|
|
||||||
array('customer', '=', true));
|
|
||||||
$models->execute_kw($db, $uid, $password,
|
$models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'search', array($domain));
|
'res.partner', 'search', array(
|
||||||
|
array(array('is_company', '=', true),
|
||||||
|
array('customer', '=', true))));
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
final List domain = Arrays.asList(
|
asList((Object[])models.execute("execute_kw", asList(
|
||||||
Arrays.asList("is_company", "=", true),
|
|
||||||
Arrays.asList("customer", "=", true));
|
|
||||||
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
|
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "search",
|
"res.partner", "search",
|
||||||
Arrays.asList(domain)
|
asList(asList(
|
||||||
|
asList("is_company", "=", true),
|
||||||
|
asList("customer", "=", true)))
|
||||||
)));
|
)));
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
@ -388,15 +390,18 @@ available to only retrieve a subset of all matched records.
|
||||||
|
|
||||||
$models->execute_kw($db, $uid, $password,
|
$models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'search',
|
'res.partner', 'search',
|
||||||
array($domain),
|
array(array(array('is_company', '=', true),
|
||||||
|
array('customer', '=', true))),
|
||||||
array('offset'=>10, 'limit'=>5));
|
array('offset'=>10, 'limit'=>5));
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
|
asList((Object[])models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "search",
|
"res.partner", "search",
|
||||||
Arrays.asList(domain),
|
asList(asList(
|
||||||
|
asList("is_company", "=", true),
|
||||||
|
asList("customer", "=", true))),
|
||||||
new HashMap() {{ put("offset", 10); put("limit", 5); }}
|
new HashMap() {{ put("offset", 10); put("limit", 5); }}
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
@ -431,14 +436,17 @@ only the number of records matching the query. It takes the same
|
||||||
|
|
||||||
$models->execute_kw($db, $uid, $password,
|
$models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'search_count',
|
'res.partner', 'search_count',
|
||||||
array($domain));
|
array(array(array('is_company', '=', true),
|
||||||
|
array('customer', '=', true))));
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
(Integer)models.execute("execute_kw", Arrays.asList(
|
(Integer)models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "search_count",
|
"res.partner", "search_count",
|
||||||
Arrays.asList(domain)
|
asList(asList(
|
||||||
|
asList("is_company", "=", true),
|
||||||
|
asList("customer", "=", true)))
|
||||||
));
|
));
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
@ -488,7 +496,8 @@ which tends to be a huge amount.
|
||||||
|
|
||||||
$ids = $models->execute_kw($db, $uid, $password,
|
$ids = $models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'search',
|
'res.partner', 'search',
|
||||||
array($domain),
|
array(array(array('is_company', '=', true),
|
||||||
|
array('customer', '=', true))),
|
||||||
array('limit'=>1));
|
array('limit'=>1));
|
||||||
$records = $models->execute_kw($db, $uid, $password,
|
$records = $models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'read', array($ids));
|
'res.partner', 'read', array($ids));
|
||||||
|
@ -497,17 +506,19 @@ which tends to be a huge amount.
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
final List ids = Arrays.asList((Object[])models.execute(
|
final List ids = asList((Object[])models.execute(
|
||||||
"execute_kw", Arrays.asList(
|
"execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "search",
|
"res.partner", "search",
|
||||||
Arrays.asList(domain),
|
asList(asList(
|
||||||
|
asList("is_company", "=", true),
|
||||||
|
asList("customer", "=", true))),
|
||||||
new HashMap() {{ put("limit", 1); }})));
|
new HashMap() {{ put("limit", 1); }})));
|
||||||
final Map record = (Map)((Object[])models.execute(
|
final Map record = (Map)((Object[])models.execute(
|
||||||
"execute_kw", Arrays.asList(
|
"execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "read",
|
"res.partner", "read",
|
||||||
Arrays.asList(ids)
|
asList(ids)
|
||||||
)
|
)
|
||||||
))[0];
|
))[0];
|
||||||
// count the number of fields fetched by default
|
// count the number of fields fetched by default
|
||||||
|
@ -542,12 +553,12 @@ Conversedly, picking only three fields deemed interesting.
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
|
asList((Object[])models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "read",
|
"res.partner", "read",
|
||||||
Arrays.asList(ids),
|
asList(ids),
|
||||||
new HashMap() {{
|
new HashMap() {{
|
||||||
put("fields", Arrays.asList("name", "country_id", "comment"));
|
put("fields", asList("name", "country_id", "comment"));
|
||||||
}}
|
}}
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
@ -574,53 +585,32 @@ updating a record):
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
|
models.execute_kw(
|
||||||
# filter keys of field attributes for display
|
db, uid, password, 'res.partner', 'fields_get',
|
||||||
{field: {
|
[], {'attributes': ['string', 'help', 'type']})
|
||||||
k: v for k, v in attributes.iteritems()
|
|
||||||
if k in ['string', 'help', 'type']
|
|
||||||
}
|
|
||||||
for field, attributes in fields.iteritems()}
|
|
||||||
|
|
||||||
.. code-block:: ruby
|
.. code-block:: ruby
|
||||||
|
|
||||||
fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
|
models.execute_kw(
|
||||||
# filter keys of field attributes for display
|
db, uid, password, 'res.partner', 'fields_get',
|
||||||
fields.each {|k, v|
|
[], {attributes: %w(string help type)})
|
||||||
fields[k] = v.keep_if {|kk, vv| %w(string help type).include? kk}
|
|
||||||
}
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$fields_full = $models->execute_kw($db, $uid, $password,
|
$models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'fields_get', array());
|
'res.partner', 'fields_get',
|
||||||
// filter keys of field attributes for display
|
array(), array('attributes' => array('string', 'help', 'type')));
|
||||||
$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
|
.. code-block:: java
|
||||||
|
|
||||||
final Map<String, Map<String, Object>> fields =
|
(Map<String, Map<String, Object>>)models.execute("execute_kw", asList(
|
||||||
(Map<String, Map<String, Object>>)models.execute("execute_kw", Arrays.asList(
|
db, uid, password,
|
||||||
db, uid, password,
|
"res.partner", "fields_get",
|
||||||
"res.partner", "fields_get",
|
emptyList(),
|
||||||
Collections.emptyList()));
|
new HashMap() {{
|
||||||
// filter keys of field attributes for display
|
put("attributes", asList("string", "help", "type"));
|
||||||
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
|
.. code-block:: json
|
||||||
|
|
||||||
|
@ -668,10 +658,11 @@ Because that is a very common task, Odoo provides a
|
||||||
:meth:`~openerp.models.Model.search_read` shortcut which as its name notes is
|
:meth:`~openerp.models.Model.search_read` shortcut which as its name notes is
|
||||||
equivalent to a :meth:`~openerp.models.Model.search` followed by a
|
equivalent to a :meth:`~openerp.models.Model.search` followed by a
|
||||||
:meth:`~openerp.models.Model.read`, but avoids having to perform two requests
|
:meth:`~openerp.models.Model.read`, but avoids having to perform two requests
|
||||||
and keep ids around. Its arguments are similar to
|
and keep ids around.
|
||||||
: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
|
Its arguments are similar to :meth:`~openerp.models.Model.search`'s, but it
|
||||||
provided it'll fetch all fields of matched records):
|
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
|
.. rst-class:: switchable
|
||||||
|
|
||||||
|
@ -693,17 +684,20 @@ provided it'll fetch all fields of matched records):
|
||||||
|
|
||||||
$models->execute_kw($db, $uid, $password,
|
$models->execute_kw($db, $uid, $password,
|
||||||
'res.partner', 'search_read',
|
'res.partner', 'search_read',
|
||||||
array($domain),
|
array(array(array('is_company', '=', true),
|
||||||
|
array('customer', '=', true))),
|
||||||
array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
|
array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
|
asList((Object[])models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "search_read",
|
"res.partner", "search_read",
|
||||||
Arrays.asList(domain),
|
asList(asList(
|
||||||
|
asList("is_company", "=", true),
|
||||||
|
asList("customer", "=", true))),
|
||||||
new HashMap() {{
|
new HashMap() {{
|
||||||
put("fields", Arrays.asList("name", "country_id", "comment"));
|
put("fields", asList("name", "country_id", "comment"));
|
||||||
put("limit", 5);
|
put("limit", 5);
|
||||||
}}
|
}}
|
||||||
)));
|
)));
|
||||||
|
@ -747,6 +741,13 @@ provided it'll fetch all fields of matched records):
|
||||||
Create records
|
Create records
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
Records of a model are created using :meth:`~openerp.models.Model.create`. The
|
||||||
|
method will create a single record and return its database identifier.
|
||||||
|
|
||||||
|
:meth:`~openerp.models.Model.create` takes a mapping of fields to values, used
|
||||||
|
to initialize the record. For any field which has a default value and is not
|
||||||
|
set through the mapping argument, the default value will be used.
|
||||||
|
|
||||||
.. rst-class:: switchable
|
.. rst-class:: switchable
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -769,19 +770,40 @@ Create records
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
final Integer id = (Integer)models.execute("execute_kw", Arrays.asList(
|
final Integer id = (Integer)models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "create",
|
"res.partner", "create",
|
||||||
Arrays.asList(new HashMap() {{ put("name", "New Partner"); }})
|
asList(new HashMap() {{ put("name", "New Partner"); }})
|
||||||
));
|
));
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
78
|
78
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
while most value types are what would be expected (integer for
|
||||||
|
:class:`~openerp.fields.Integer`, string for :class:`~openerp.fields.Char`
|
||||||
|
or :class:`~openerp.fields.Text`),
|
||||||
|
|
||||||
|
* :class:`~openerp.fields.Date`, :class:`~openerp.fields.Datetime` and
|
||||||
|
:class:`~openerp.fields.Binary` fields use string values
|
||||||
|
* :class:`~openerp.fields.One2many` and :class:`~openerp.fields.Many2many`
|
||||||
|
use a special command protocol detailed in :meth:`the documentation to
|
||||||
|
the write method <openerp.models.Model.write>`.
|
||||||
|
|
||||||
Update records
|
Update records
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
Reccords can be updated using :meth:`~openerp.models.Model.write`, it takes
|
||||||
|
a list of records to update and a mapping of updated fields to values similar
|
||||||
|
to :meth:`~openerp.models.Model.create`.
|
||||||
|
|
||||||
|
Multiple records can be updated simultanously, but they will all get the same
|
||||||
|
values for the fields being set. It is not currently possible to perform
|
||||||
|
"computed" updates (where the value being set depends on an existing value of
|
||||||
|
a record).
|
||||||
|
|
||||||
.. rst-class:: switchable
|
.. rst-class:: switchable
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -810,19 +832,19 @@ Update records
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
models.execute("execute_kw", Arrays.asList(
|
models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "write",
|
"res.partner", "write",
|
||||||
Arrays.asList(
|
asList(
|
||||||
Arrays.asList(id),
|
asList(id),
|
||||||
new HashMap() {{ put("name", "Newer Partner"); }}
|
new HashMap() {{ put("name", "Newer Partner"); }}
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
// get record name after having changed it
|
// get record name after having changed it
|
||||||
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
|
asList((Object[])models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "name_get",
|
"res.partner", "name_get",
|
||||||
Arrays.asList(Arrays.asList(id))
|
asList(asList(id))
|
||||||
)));
|
)));
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
@ -832,6 +854,9 @@ Update records
|
||||||
Delete records
|
Delete records
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
Records can be deleted in bulk by providing the ids of all records to remove
|
||||||
|
to :meth:`~openerp.models.Model.unlink`.
|
||||||
|
|
||||||
.. rst-class:: switchable
|
.. rst-class:: switchable
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -860,20 +885,561 @@ Delete records
|
||||||
|
|
||||||
.. code-block:: java
|
.. code-block:: java
|
||||||
|
|
||||||
models.execute("execute_kw", Arrays.asList(
|
models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "unlink",
|
"res.partner", "unlink",
|
||||||
Arrays.asList(Arrays.asList(id))));
|
asList(asList(id))));
|
||||||
// check if the deleted record is still in the database
|
// check if the deleted record is still in the database
|
||||||
Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
|
asList((Object[])models.execute("execute_kw", asList(
|
||||||
db, uid, password,
|
db, uid, password,
|
||||||
"res.partner", "search",
|
"res.partner", "search",
|
||||||
Arrays.asList(Arrays.asList(Arrays.asList("id", "=", 78)))
|
asList(asList(asList("id", "=", 78)))
|
||||||
)));
|
)));
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
Inspection and introspection
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. todo:: ``get_external_id`` is kinda crap and may not return an id: it just
|
||||||
|
gets a random existing xid but won't generate one if there is no
|
||||||
|
xid currently associated with the record. And operating with xids
|
||||||
|
isn't exactly fun in RPC.
|
||||||
|
|
||||||
|
While we previously used :meth:`~openerp.models.Model.fields_get` to query a
|
||||||
|
model's and have been using an arbitrary model from the start, Odoo stores
|
||||||
|
most model metadata inside a few meta-models which allow both querying the
|
||||||
|
system and altering models and fields (with some limitations) on the fly over
|
||||||
|
XML-RPC.
|
||||||
|
|
||||||
|
.. _reference/webservice/inspection/models:
|
||||||
|
|
||||||
|
``ir.model``
|
||||||
|
''''''''''''
|
||||||
|
|
||||||
|
Provides informations about Odoo models themselves via its various fields
|
||||||
|
|
||||||
|
``name``
|
||||||
|
a human-readable description of the model
|
||||||
|
``model``
|
||||||
|
the name of each model in the system
|
||||||
|
``state``
|
||||||
|
whether the model was generated in Python code (``base``) or by creating
|
||||||
|
an ``ir.model`` record (``manual``)
|
||||||
|
``field_id``
|
||||||
|
list of the model's fields through a :class:`~openerp.fields.One2many` to
|
||||||
|
:ref:`reference/webservice/inspection/fields`
|
||||||
|
``view_ids``
|
||||||
|
:class:`~openerp.fields.One2many` to the :ref:`reference/views` defined
|
||||||
|
for the model
|
||||||
|
``access_ids``
|
||||||
|
:class:`~openerp.fields.One2many` relation to the
|
||||||
|
:ref:`reference/security/acl` set on the model
|
||||||
|
|
||||||
|
``ir.model`` can be used to
|
||||||
|
|
||||||
|
* query the system for installed models (as a precondition to operations
|
||||||
|
on the model or to explore the system's content)
|
||||||
|
* get information about a specific model (generally by listing the fields
|
||||||
|
associated with it)
|
||||||
|
* create new models dynamically over RPC
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
* "custom" model names must start with ``x_``
|
||||||
|
* the ``state`` must be provided and ``manual``, otherwise the model will
|
||||||
|
not be loaded
|
||||||
|
* it is not possible to add new *methods* to a custom model, only fields
|
||||||
|
|
||||||
|
.. rst-class:: force-right
|
||||||
|
|
||||||
|
a custom model will initially contain only the "built-in" fields available
|
||||||
|
on all models:
|
||||||
|
|
||||||
|
.. rst-class:: switchable
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
models.execute_kw(db, uid, password, 'ir.model', 'create', [{
|
||||||
|
'name': "Custom Model",
|
||||||
|
'model': "x_custom_model",
|
||||||
|
'state': 'manual',
|
||||||
|
}])
|
||||||
|
models.execute_kw(
|
||||||
|
db, uid, password, 'x_custom_model', 'fields_get',
|
||||||
|
[], {'attributes': ['string', 'help', 'type']})
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'ir.model', 'create', array(array(
|
||||||
|
'name' => "Custom Model",
|
||||||
|
'model' => 'x_custom_model',
|
||||||
|
'state' => 'manual'
|
||||||
|
))
|
||||||
|
);
|
||||||
|
$models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'x_custom_model', 'fields_get',
|
||||||
|
array(),
|
||||||
|
array('attributes' => array('string', 'help', 'type'))
|
||||||
|
);
|
||||||
|
|
||||||
|
.. code-block:: ruby
|
||||||
|
|
||||||
|
models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'ir.model', 'create', [{
|
||||||
|
name: "Custom Model",
|
||||||
|
model: 'x_custom_model',
|
||||||
|
state: 'manual'
|
||||||
|
}])
|
||||||
|
fields = models.execute_kw(
|
||||||
|
db, uid, password, 'x_custom_model', 'fields_get',
|
||||||
|
[], {attributes: %w(string help type)})
|
||||||
|
|
||||||
|
.. code-block:: java
|
||||||
|
|
||||||
|
models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"ir.model", "create",
|
||||||
|
asList(new HashMap<String, Object>() {{
|
||||||
|
put("name", "Custom Model");
|
||||||
|
put("model", "x_custom_model");
|
||||||
|
put("state", "manual");
|
||||||
|
}})
|
||||||
|
));
|
||||||
|
final Object fields = models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"x_custom_model", "fields_get",
|
||||||
|
emptyList(),
|
||||||
|
new HashMap<String, Object> () {{
|
||||||
|
put("attributes", asList(
|
||||||
|
"string",
|
||||||
|
"help",
|
||||||
|
"type"));
|
||||||
|
}}
|
||||||
|
));
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"create_uid": {
|
||||||
|
"type": "many2one",
|
||||||
|
"string": "Created by"
|
||||||
|
},
|
||||||
|
"create_date": {
|
||||||
|
"type": "datetime",
|
||||||
|
"string": "Created on"
|
||||||
|
},
|
||||||
|
"__last_update": {
|
||||||
|
"type": "datetime",
|
||||||
|
"string": "Last Modified on"
|
||||||
|
},
|
||||||
|
"write_uid": {
|
||||||
|
"type": "many2one",
|
||||||
|
"string": "Last Updated by"
|
||||||
|
},
|
||||||
|
"write_date": {
|
||||||
|
"type": "datetime",
|
||||||
|
"string": "Last Updated on"
|
||||||
|
},
|
||||||
|
"display_name": {
|
||||||
|
"type": "char",
|
||||||
|
"string": "Display Name"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"string": "Id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. _reference/webservice/inspection/fields:
|
||||||
|
|
||||||
|
``ir.model.fields``
|
||||||
|
'''''''''''''''''''
|
||||||
|
|
||||||
|
Provides informations about the fields of Odoo models and allows adding
|
||||||
|
custom fields without using Python code
|
||||||
|
|
||||||
|
``model_id``
|
||||||
|
:class:`~openerp.fields.Many2one` to
|
||||||
|
:ref:`reference/webservice/inspection/models` to which the field belongs
|
||||||
|
``name``
|
||||||
|
the field's technical name (used in ``read`` or ``write``)
|
||||||
|
``field_description``
|
||||||
|
the field's user-readable label (e.g. ``string`` in ``fields_get``)
|
||||||
|
``ttype``
|
||||||
|
the :ref:`type <reference/orm/fields>` of field to create
|
||||||
|
``state``
|
||||||
|
whether the field was created via Python code (``base``) or via
|
||||||
|
``ir.model.fields`` (``manual``)
|
||||||
|
``required``, ``readonly``, ``translate``
|
||||||
|
enables the corresponding flag on the field
|
||||||
|
``groups``
|
||||||
|
:ref:`field-level access control <reference/security/fields>`, a
|
||||||
|
:class:`~openerp.fields.Many2many` to ``res.groups``
|
||||||
|
``selection``, ``size``, ``on_delete``, ``relation``, ``relation_field``, ``domain``
|
||||||
|
type-specific properties and customizations, see :ref:`the fields
|
||||||
|
documentation <reference/orm/fields>` for details
|
||||||
|
|
||||||
|
Like custom models, only new fields created with ``state="manual"`` are
|
||||||
|
activated as actual fields on the model.
|
||||||
|
|
||||||
|
.. warning:: computed fields can not be added via ``ir.model.fields``, some
|
||||||
|
field meta-information (defaults, onchange) can not be set either
|
||||||
|
|
||||||
|
.. todo:: maybe new-API fields could store constant ``default`` in a new
|
||||||
|
column, maybe JSON-encoded?
|
||||||
|
|
||||||
|
.. rst-class:: switchable
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
id = models.execute_kw(db, uid, password, 'ir.model', 'create', [{
|
||||||
|
'name': "Custom Model",
|
||||||
|
'model': "x_custom",
|
||||||
|
'state': 'manual',
|
||||||
|
}])
|
||||||
|
models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'ir.model.fields', 'create', [{
|
||||||
|
'model_id': id,
|
||||||
|
'name': 'x_name',
|
||||||
|
'ttype': 'char',
|
||||||
|
'state': 'manual',
|
||||||
|
'required': True,
|
||||||
|
}])
|
||||||
|
record_id = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'x_custom', 'create', [{
|
||||||
|
'x_name': "test record",
|
||||||
|
}])
|
||||||
|
models.execute_kw(db, uid, password, 'x_custom', 'read', [[record_id]])
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$id = $models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'ir.model', 'create', array(array(
|
||||||
|
'name' => "Custom Model",
|
||||||
|
'model' => 'x_custom',
|
||||||
|
'state' => 'manual'
|
||||||
|
))
|
||||||
|
);
|
||||||
|
$models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'ir.model.fields', 'create', array(array(
|
||||||
|
'model_id' => $id,
|
||||||
|
'name' => 'x_name',
|
||||||
|
'ttype' => 'char',
|
||||||
|
'state' => 'manual',
|
||||||
|
'required' => true
|
||||||
|
))
|
||||||
|
);
|
||||||
|
$record_id = $models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'x_custom', 'create', array(array(
|
||||||
|
'x_name' => "test record"
|
||||||
|
))
|
||||||
|
);
|
||||||
|
$models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'x_custom', 'read',
|
||||||
|
array(array($record_id)));
|
||||||
|
|
||||||
|
.. code-block:: ruby
|
||||||
|
|
||||||
|
id = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'ir.model', 'create', [{
|
||||||
|
name: "Custom Model",
|
||||||
|
model: "x_custom",
|
||||||
|
state: 'manual'
|
||||||
|
}])
|
||||||
|
models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'ir.model.fields', 'create', [{
|
||||||
|
model_id: id,
|
||||||
|
name: "x_name",
|
||||||
|
ttype: "char",
|
||||||
|
state: "manual",
|
||||||
|
required: true
|
||||||
|
}])
|
||||||
|
record_id = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'x_custom', 'create', [{
|
||||||
|
x_name: "test record"
|
||||||
|
}])
|
||||||
|
models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'x_custom', 'read', [[record_id]])
|
||||||
|
|
||||||
|
.. code-block:: java
|
||||||
|
|
||||||
|
final Integer id = (Integer)models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"ir.model", "create",
|
||||||
|
asList(new HashMap<String, Object>() {{
|
||||||
|
put("name", "Custom Model");
|
||||||
|
put("model", "x_custom");
|
||||||
|
put("state", "manual");
|
||||||
|
}})
|
||||||
|
));
|
||||||
|
models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"ir.model.fields", "create",
|
||||||
|
asList(new HashMap<String, Object>() {{
|
||||||
|
put("model_id", id);
|
||||||
|
put("name", "x_name");
|
||||||
|
put("ttype", "char");
|
||||||
|
put("state", "manual");
|
||||||
|
put("required", true);
|
||||||
|
}})
|
||||||
|
));
|
||||||
|
final Integer record_id = (Integer)models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"x_custom", "create",
|
||||||
|
asList(new HashMap<String, Object>() {{
|
||||||
|
put("x_name", "test record");
|
||||||
|
}})
|
||||||
|
));
|
||||||
|
|
||||||
|
client.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"x_custom", "read",
|
||||||
|
asList(asList(record_id))
|
||||||
|
));
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"create_uid": [1, "Administrator"],
|
||||||
|
"x_name": "test record",
|
||||||
|
"__last_update": "2014-11-12 16:32:13",
|
||||||
|
"write_uid": [1, "Administrator"],
|
||||||
|
"write_date": "2014-11-12 16:32:13",
|
||||||
|
"create_date": "2014-11-12 16:32:13",
|
||||||
|
"id": 1,
|
||||||
|
"display_name": "test record"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Workflow manipulations
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
:ref:`reference/workflows` can be moved along by sending them *signals*.
|
||||||
|
Instead of using the top-level ``execute_kw``, signals are sent using
|
||||||
|
``exec_workflow``.
|
||||||
|
|
||||||
|
Signals are sent to a specific record, and possibly trigger a transition on
|
||||||
|
the workflow instance associated with the record.
|
||||||
|
|
||||||
|
.. warning:: requires that the ``account`` module be installed
|
||||||
|
:class: force-right
|
||||||
|
|
||||||
|
.. rst-class:: switchable
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
client = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'res.partner', 'search_read',
|
||||||
|
[[('customer', '=', True)]],
|
||||||
|
{'limit': 1, 'fields': [
|
||||||
|
'property_account_receivable',
|
||||||
|
'property_payment_term',
|
||||||
|
'property_account_position']
|
||||||
|
})[0]
|
||||||
|
invoice_id = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'account.invoice', 'create', [{
|
||||||
|
'partner_id': client['id'],
|
||||||
|
'account_id': client['property_account_receivable'][0],
|
||||||
|
'invoice_line': [(0, False, {'name': "AAA"})]
|
||||||
|
}])
|
||||||
|
|
||||||
|
models.exec_workflow(
|
||||||
|
db, uid, password, 'account.invoice', 'invoice_open', invoice_id)
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$client = $models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'res.partner', 'search_read',
|
||||||
|
array(array(array('customer', '=', true))),
|
||||||
|
array(
|
||||||
|
'limit' => 1,
|
||||||
|
'fields' => array(
|
||||||
|
'property_account_receivable',
|
||||||
|
'property_payment_term',
|
||||||
|
'property_account_position'
|
||||||
|
)))[0];
|
||||||
|
$invoice_id = $models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'account.invoice', 'create', array(array(
|
||||||
|
'partner_id' => $client['id'],
|
||||||
|
'account_id' => $client['property_account_receivable'][0],
|
||||||
|
'invoice_line' => array(array(0, false, array('name' => "AAA")))
|
||||||
|
)));
|
||||||
|
|
||||||
|
$models->exec_workflow(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'account.invoice', 'invoice_open',
|
||||||
|
$invoice_id);
|
||||||
|
|
||||||
|
.. code-block:: ruby
|
||||||
|
|
||||||
|
client = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'res.partner', 'search_read',
|
||||||
|
[[['customer', '=', true]]],
|
||||||
|
{limit: 1, fields: %w(property_account_receivable property_payment_term property_account_position)}
|
||||||
|
)[0]
|
||||||
|
invoice_id = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'account.invoice', 'create', [{
|
||||||
|
partner_id: client['id'],
|
||||||
|
account_id: client['property_account_receivable'][0],
|
||||||
|
invoice_line: [[0, false, {name: "AAA"}]]
|
||||||
|
}])
|
||||||
|
|
||||||
|
models.exec_workflow(
|
||||||
|
db, uid, password,
|
||||||
|
'account.invoice', 'invoice_open', invoice_id)
|
||||||
|
|
||||||
|
.. code-block:: java
|
||||||
|
|
||||||
|
final Map<String, Object> c = (Map<String, Object>)
|
||||||
|
((Object[])models.execute("execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"res.partner", "search_read",
|
||||||
|
asList(
|
||||||
|
asList(
|
||||||
|
asList("customer", "=", true))),
|
||||||
|
new HashMap<String, Object>() {{
|
||||||
|
put("limit", 1);
|
||||||
|
put("fields", asList(
|
||||||
|
"property_account_receivable",
|
||||||
|
"property_payment_term",
|
||||||
|
"property_account_position"
|
||||||
|
));
|
||||||
|
}}
|
||||||
|
)))[0];
|
||||||
|
final Integer invoice_id = (Integer)models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"account.invoice", "create",
|
||||||
|
asList(new HashMap<String, Object>() {{
|
||||||
|
put("partner_id", c.get("id"));
|
||||||
|
put("account_id", ((Object[])c.get("property_account_receivable"))[0]);
|
||||||
|
put("invoice_line", asList(
|
||||||
|
asList(0, false, new HashMap<String, Object>() {{
|
||||||
|
put("name", "AAA");
|
||||||
|
}})
|
||||||
|
));
|
||||||
|
}})
|
||||||
|
));
|
||||||
|
|
||||||
|
models.execute(
|
||||||
|
"exec_workflow", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"account.invoice", "invoice_open", invoice_id));
|
||||||
|
|
||||||
|
Report printing
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Available reports can be listed by searching the ``ir.actions.report.xml``
|
||||||
|
model, fields of interest being
|
||||||
|
|
||||||
|
``model``
|
||||||
|
the model on which the report applies, can be used to look for available
|
||||||
|
reports on a specific model
|
||||||
|
``name``
|
||||||
|
human-readable report name
|
||||||
|
``report_name``
|
||||||
|
the technical name of the report, used to print it
|
||||||
|
|
||||||
|
Reports can be printed over RPC with the following information:
|
||||||
|
|
||||||
|
* the name of the report (``report_name``)
|
||||||
|
* the ids of the records to include in the report
|
||||||
|
|
||||||
|
.. rst-class:: switchable
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
invoice_ids = models.execute_kw(
|
||||||
|
db, uid, password, 'account.invoice', 'search',
|
||||||
|
[[('type', '=', 'out_invoice'), ('state', '=', 'open')]])
|
||||||
|
report = xmlrpclib.ServerProxy('{}/xmlrpc/2/report'.format(url))
|
||||||
|
result = report.render_report(
|
||||||
|
db, uid, password, 'account.report_invoice', invoice_ids)
|
||||||
|
report_data = result['result'].decode('base64')
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$invoice_ids = $models->execute_kw(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'account.invoice', 'search',
|
||||||
|
array(array(array('type', '=', 'out_invoice'),
|
||||||
|
array('state', '=', 'open'))));
|
||||||
|
$report = ripcord::client("$url/xmlrpc/2/report");
|
||||||
|
$result = $report->render_report(
|
||||||
|
$db, $uid, $password,
|
||||||
|
'account.report_invoice', $invoice_ids);
|
||||||
|
$report_data = base64_decode($result['result']);
|
||||||
|
|
||||||
|
.. code-block:: ruby
|
||||||
|
|
||||||
|
require 'base64'
|
||||||
|
invoice_ids = models.execute_kw(
|
||||||
|
db, uid, password,
|
||||||
|
'account.invoice', 'search',
|
||||||
|
[[['type', '=', 'out_invoice'], ['state', '=', 'open']]])
|
||||||
|
report = XMLRPC::Client.new2("#{url}/xmlrpc/2/report").proxy
|
||||||
|
result = report.render_report(
|
||||||
|
db, uid, password,
|
||||||
|
'account.report_invoice', invoice_ids)
|
||||||
|
report_data = Base64.decode64(result['result'])
|
||||||
|
|
||||||
|
.. code-block:: java
|
||||||
|
|
||||||
|
final Object[] invoice_ids = (Object[])models.execute(
|
||||||
|
"execute_kw", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"account.invoice", "search",
|
||||||
|
asList(asList(
|
||||||
|
asList("type", "=", "out_invoice"),
|
||||||
|
asList("state", "=", "open")))
|
||||||
|
));
|
||||||
|
final XmlRpcClientConfigImpl report_config = new XmlRpcClientConfigImpl();
|
||||||
|
report_config.setServerURL(
|
||||||
|
new URL(String.format("%s/xmlrpc/2/report", url)));
|
||||||
|
final Map<String, Object> result = (Map<String, Object>)client.execute(
|
||||||
|
report_config, "render_report", asList(
|
||||||
|
db, uid, password,
|
||||||
|
"account.report_invoice",
|
||||||
|
invoice_ids));
|
||||||
|
final byte[] report_data = DatatypeConverter.parseBase64Binary(
|
||||||
|
(String)result.get("result"));
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
:class: force-right
|
||||||
|
|
||||||
|
the report is sent as PDF binary data encoded in base64_, it must be
|
||||||
|
decoded and may need to be saved to disk before use
|
||||||
|
|
||||||
.. _PostgreSQL: http://www.postgresql.org
|
.. _PostgreSQL: http://www.postgresql.org
|
||||||
.. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC
|
.. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC
|
||||||
|
.. _base64: http://en.wikipedia.org/wiki/Base64
|
||||||
|
|
|
@ -71,6 +71,8 @@ This means the first *group rule* restricts access, but any further
|
||||||
|
|
||||||
although access rules do
|
although access rules do
|
||||||
|
|
||||||
|
.. _reference/security/fields:
|
||||||
|
|
||||||
Field Access
|
Field Access
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,8 @@ defined (in addition to the Odoo ``safe_eval`` environment):
|
||||||
- all the model column names, and
|
- all the model column names, and
|
||||||
- all the browse record's attributes.
|
- all the browse record's attributes.
|
||||||
|
|
||||||
|
.. _reference/workflows/signals:
|
||||||
|
|
||||||
Signals
|
Signals
|
||||||
'''''''
|
'''''''
|
||||||
|
|
||||||
|
|
|
@ -1057,8 +1057,8 @@ class Char(_String):
|
||||||
return ustr(value)[:self.size]
|
return ustr(value)[:self.size]
|
||||||
|
|
||||||
class Text(_String):
|
class Text(_String):
|
||||||
""" Text field. Very similar to :class:`~.Char` but used for longer
|
""" Very similar to :class:`~.Char` but used for longer contents, does not
|
||||||
contents and displayed as a multiline text box
|
have a size and usually displayed as a multiline text box.
|
||||||
|
|
||||||
:param translate: whether the value of this field can be translated
|
:param translate: whether the value of this field can be translated
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2994,8 +2994,8 @@ class BaseModel(object):
|
||||||
elif 'x_name' in cls._fields:
|
elif 'x_name' in cls._fields:
|
||||||
cls._rec_name = 'x_name'
|
cls._rec_name = 'x_name'
|
||||||
|
|
||||||
def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
|
def fields_get(self, cr, user, allfields=None, context=None, write_access=True, attributes=None):
|
||||||
""" fields_get([fields])
|
""" fields_get([fields][, attributes])
|
||||||
|
|
||||||
Return the definition of each field.
|
Return the definition of each field.
|
||||||
|
|
||||||
|
@ -3003,16 +3003,14 @@ class BaseModel(object):
|
||||||
dictionaries. The _inherits'd fields are included. The string, help,
|
dictionaries. The _inherits'd fields are included. The string, help,
|
||||||
and selection (if present) attributes are translated.
|
and selection (if present) attributes are translated.
|
||||||
|
|
||||||
:param cr: database cursor
|
:param allfields: list of fields to document, all if empty or not provided
|
||||||
:param user: current user id
|
:param attributes: list of description attributes to return for each field, all if empty or not provided
|
||||||
:param allfields: list of fields
|
|
||||||
:param context: context arguments, like lang, time zone
|
|
||||||
:return: dictionary of field dictionaries, each one describing a field of the business object
|
|
||||||
:raise AccessError: * if user has no create/write rights on the requested object
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
recs = self.browse(cr, user, [], context)
|
recs = self.browse(cr, user, [], context)
|
||||||
|
|
||||||
|
has_access = functools.partial(recs.check_access_rights, raise_exception=False)
|
||||||
|
readonly = not (has_access('write') or has_access('create'))
|
||||||
|
|
||||||
res = {}
|
res = {}
|
||||||
for fname, field in self._fields.iteritems():
|
for fname, field in self._fields.iteritems():
|
||||||
if allfields and fname not in allfields:
|
if allfields and fname not in allfields:
|
||||||
|
@ -3021,14 +3019,15 @@ class BaseModel(object):
|
||||||
continue
|
continue
|
||||||
if field.groups and not recs.user_has_groups(field.groups):
|
if field.groups and not recs.user_has_groups(field.groups):
|
||||||
continue
|
continue
|
||||||
res[fname] = field.get_description(recs.env)
|
|
||||||
|
|
||||||
# if user cannot create or modify records, make all fields readonly
|
description = field.get_description(recs.env)
|
||||||
has_access = functools.partial(recs.check_access_rights, raise_exception=False)
|
if readonly:
|
||||||
if not (has_access('write') or has_access('create')):
|
|
||||||
for description in res.itervalues():
|
|
||||||
description['readonly'] = True
|
description['readonly'] = True
|
||||||
description['states'] = {}
|
description['states'] = {}
|
||||||
|
if attributes:
|
||||||
|
description = {k: v for k, v in description.iteritems()
|
||||||
|
if k in attributes}
|
||||||
|
res[fname] = description
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@ -3604,38 +3603,67 @@ class BaseModel(object):
|
||||||
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
|
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
|
||||||
:raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
|
:raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
|
||||||
|
|
||||||
.. _openerp/models/relationals/format:
|
* For numeric fields (:class:`~openerp.fields.Integer`,
|
||||||
|
:class:`~openerp.fields.Float`) the value should be of the
|
||||||
|
corresponding type
|
||||||
|
* For :class:`~openerp.fields.Boolean`, the value should be a
|
||||||
|
:class:`python:bool`
|
||||||
|
* For :class:`~openerp.fields.Selection`, the value should match the
|
||||||
|
selection values (generally :class:`python:str`, sometimes
|
||||||
|
:class:`python:int`)
|
||||||
|
* For :class:`~openerp.fields.Many2one`, the value should be the
|
||||||
|
database identifier of the record to set
|
||||||
|
* Other non-relational fields use a string for value
|
||||||
|
|
||||||
.. note:: Relational fields use a special "commands" format to manipulate their values
|
.. danger::
|
||||||
|
|
||||||
This format is a list of command triplets executed sequentially,
|
for historical and compatibility reasons,
|
||||||
possible command triplets are:
|
:class:`~openerp.fields.Date` and
|
||||||
|
:class:`~openerp.fields.Datetime` fields use strings as values
|
||||||
|
(written and read) rather than :class:`~python:datetime.date` or
|
||||||
|
:class:`~python:datetime.datetime`. These date strings are
|
||||||
|
UTC-only and formatted according to
|
||||||
|
:const:`openerp.tools.misc.DEFAULT_SERVER_DATE_FORMAT` and
|
||||||
|
:const:`openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT`
|
||||||
|
* .. _openerp/models/relationals/format:
|
||||||
|
|
||||||
``(0, _, values: dict)``
|
:class:`~openerp.fields.One2many` and
|
||||||
links to a new record created from the provided values
|
:class:`~openerp.fields.Many2many` use a special "commands" format to
|
||||||
``(1, id, values: dict)``
|
manipulate the set of records stored in/associated with the field.
|
||||||
updates the already-linked record of id ``id`` with the
|
|
||||||
provided ``values``
|
|
||||||
``(2, id, _)``
|
|
||||||
unlinks and deletes the linked record of id ``id``
|
|
||||||
``(3, id, _)``
|
|
||||||
unlinks the linked record of id ``id`` without deleting it
|
|
||||||
``(4, id, _)``
|
|
||||||
links to an existing record of id ``id``
|
|
||||||
``(5, _, _)``
|
|
||||||
unlinks all records in the relation, equivalent to using
|
|
||||||
the command ``3`` on every linked record
|
|
||||||
``(6, _, ids)``
|
|
||||||
replaces the existing list of linked records by the provoded
|
|
||||||
ones, equivalent to using ``5`` then ``4`` for each id in
|
|
||||||
``ids``)
|
|
||||||
|
|
||||||
(in command triplets, ``_`` values are ignored and can be
|
This format is a list of triplets executed sequentially, where each
|
||||||
anything, generally ``0`` or ``False``)
|
triplet is a command to execute on the set of records. Not all
|
||||||
|
commands apply in all situations. Possible commands are:
|
||||||
|
|
||||||
Any command can be used on :class:`~openerp.fields.Many2many`,
|
``(0, _, values)``
|
||||||
only ``0``, ``1`` and ``2`` can be used on
|
adds a new record created from the provided ``value`` dict.
|
||||||
:class:`~openerp.fields.One2many`.
|
``(1, id, values)``
|
||||||
|
updates an existing record of id ``id`` with the values in
|
||||||
|
``values``. Can not be used in :meth:`~.create`.
|
||||||
|
``(2, id, _)``
|
||||||
|
removes the record of id ``id`` from the set, then deletes it
|
||||||
|
(from the database). Can not be used in :meth:`~.create`.
|
||||||
|
``(3, id, _)``
|
||||||
|
removes the record of id ``id`` from the set, but does not
|
||||||
|
delete it. Can not be used on
|
||||||
|
:class:`~openerp.fields.One2many`. Can not be used in
|
||||||
|
:meth:`~.create`.
|
||||||
|
``(4, id, _)``
|
||||||
|
adds an existing record of id ``id`` to the set. Can not be
|
||||||
|
used on :class:`~openerp.fields.One2many`.
|
||||||
|
``(5, _, _)``
|
||||||
|
removes all records from the set, equivalent to using the
|
||||||
|
command ``3`` on every record explicitly. Can not be used on
|
||||||
|
:class:`~openerp.fields.One2many`. Can not be used in
|
||||||
|
:meth:`~.create`.
|
||||||
|
``(6, _, ids)``
|
||||||
|
replaces all existing records in the set by the ``ids`` list,
|
||||||
|
equivalent to using the command ``5`` followed by a command
|
||||||
|
``4`` for each ``id`` in ``ids``. Can not be used on
|
||||||
|
:class:`~openerp.fields.One2many`.
|
||||||
|
|
||||||
|
.. note:: Values marked as ``_`` in the list above are ignored and
|
||||||
|
can be anything, generally ``0`` or ``False``.
|
||||||
"""
|
"""
|
||||||
if not self:
|
if not self:
|
||||||
return True
|
return True
|
||||||
|
|
Loading…
Reference in New Issue