[IMP] doc: improve index, WS page
This commit is contained in:
commit
c92e70b929
|
@ -32,7 +32,7 @@
|
|||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
{{ toctree(maxdepth=4, collapse=False, includehidden=True,
|
||||
main_navbar=False, titles_only=False) }}
|
||||
navbar='side', titles_only=False) }}
|
||||
{% if github_link %}
|
||||
<p><a href="{{ github_link(mode='edit') }}" class="github">
|
||||
Edit on GitHub
|
||||
|
@ -72,7 +72,8 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<nav class="collapse navbar-collapse navbar-main" role="navigation">
|
||||
{{ toctree(titles_only=True, maxdepth=2, includehidden=True, collapse=False) }}
|
||||
{{ toctree(titles_only=True, maxdepth=2, includehidden=True,
|
||||
collapse=False, navbar='main') }}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sphinx.roles
|
||||
import sphinx.environment
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
from docutils.writers.html4css1 import HTMLTranslator as DocutilsTranslator
|
||||
|
||||
|
@ -9,27 +8,17 @@ def patch():
|
|||
# navify toctree (oh god)
|
||||
@monkey(sphinx.environment.BuildEnvironment)
|
||||
def resolve_toctree(old_resolve, self, *args, **kwargs):
|
||||
""" If main_navbar, bootstrapify TOC to yield a navbar
|
||||
""" If navbar, bootstrapify TOC to yield a navbar
|
||||
|
||||
"""
|
||||
main_navbar = kwargs.pop('main_navbar', False)
|
||||
navbar = kwargs.pop('navbar', None)
|
||||
toc = old_resolve(self, *args, **kwargs)
|
||||
if toc is None:
|
||||
return None
|
||||
|
||||
navbarify(toc[0], main_navbar=main_navbar)
|
||||
navbarify(toc[0], navbar=navbar)
|
||||
return toc
|
||||
|
||||
@monkey(StandaloneHTMLBuilder)
|
||||
def _get_local_toctree(old_local, self, *args, **kwargs):
|
||||
""" _get_local_toctree generates a documentation toctree for the local
|
||||
document (?), called from handle_page
|
||||
"""
|
||||
# so can call toctree(main_navbar=False)
|
||||
d = {'main_navbar': True}
|
||||
d.update(kwargs)
|
||||
return old_local(self, *args, **d)
|
||||
|
||||
# monkeypatch visit_table to remove border and add .table
|
||||
HTMLTranslator.visit_table = visit_table
|
||||
# disable colspec crap
|
||||
|
@ -37,11 +26,18 @@ def patch():
|
|||
# copy data- attributes straight from source to dest
|
||||
HTMLTranslator.starttag = starttag_data
|
||||
|
||||
def navbarify(node, main_navbar=False):
|
||||
# add classes to toplevel
|
||||
if not main_navbar:
|
||||
navify([node])
|
||||
else:
|
||||
def navbarify(node, navbar=None):
|
||||
"""
|
||||
:param node: toctree node to navbarify
|
||||
:param navbar: Whether this toctree is a 'main' navbar, a 'side' navbar or
|
||||
not a navbar at all
|
||||
"""
|
||||
if navbar == 'side':
|
||||
for n in node.traverse():
|
||||
if n.tagname == 'bullet_list':
|
||||
n['classes'].append('nav')
|
||||
elif navbar == 'main':
|
||||
# add classes to just toplevel
|
||||
node['classes'].extend(['nav', 'navbar-nav', 'navbar-right'])
|
||||
for list_item in node.children:
|
||||
# bullet_list
|
||||
|
@ -52,6 +48,10 @@ def navbarify(node, main_navbar=False):
|
|||
# list_item
|
||||
# compact_paragraph
|
||||
# reference
|
||||
# no bullet_list.list_item -> don't dropdownify
|
||||
if not list_item.children[1].children:
|
||||
return
|
||||
|
||||
list_item['classes'].append('dropdown')
|
||||
# list_item.compact_paragraph.reference
|
||||
link = list_item.children[0].children[0]
|
||||
|
@ -59,11 +59,6 @@ def navbarify(node, main_navbar=False):
|
|||
link.attributes['data-toggle'] = 'dropdown'
|
||||
# list_item.bullet_list
|
||||
list_item.children[1]['classes'].append('dropdown-menu')
|
||||
def navify(nodes):
|
||||
for node in nodes:
|
||||
if node.tagname == 'bullet_list':
|
||||
node['classes'].append('nav')
|
||||
navify(node.children)
|
||||
|
||||
def visit_table(self, node):
|
||||
"""
|
||||
|
|
|
@ -5902,8 +5902,10 @@ button.close {
|
|||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after,
|
||||
.document-super:before,
|
||||
.document-super:after,
|
||||
.document-super:not(.stripe):before,
|
||||
.document-super:not(.stripe):after,
|
||||
.document-super.stripe:before,
|
||||
.document-super.stripe:after,
|
||||
.document:before,
|
||||
.document:after {
|
||||
content: " ";
|
||||
|
@ -5924,7 +5926,8 @@ button.close {
|
|||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after,
|
||||
.document-super:after,
|
||||
.document-super:not(.stripe):after,
|
||||
.document-super.stripe:after,
|
||||
.document:after {
|
||||
clear: both;
|
||||
}
|
||||
|
@ -6181,35 +6184,53 @@ body {
|
|||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
.document-super {
|
||||
.document-super:not(.stripe) {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.document-super {
|
||||
.document-super:not(.stripe) {
|
||||
width: 750px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.document-super {
|
||||
.document-super:not(.stripe) {
|
||||
width: 970px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.document-super {
|
||||
.document-super:not(.stripe) {
|
||||
width: 1170px;
|
||||
}
|
||||
}
|
||||
.document-super > .navbar-header,
|
||||
.document-super > .navbar-collapse {
|
||||
.document-super:not(.stripe) > .navbar-header,
|
||||
.document-super:not(.stripe) > .navbar-collapse {
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.document-super > .navbar-header,
|
||||
.document-super > .navbar-collapse {
|
||||
.document-super:not(.stripe) > .navbar-header,
|
||||
.document-super:not(.stripe) > .navbar-collapse {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
.document-super.stripe {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.document-super.stripe > .navbar-header,
|
||||
.document-super.stripe > .navbar-collapse {
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.document-super.stripe > .navbar-header,
|
||||
.document-super.stripe > .navbar-collapse {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
@ -6296,7 +6317,7 @@ body {
|
|||
-o-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
.docs-nav .navbar-nav > li > a:after {
|
||||
.docs-nav .navbar-nav > li.dropdown > a:after {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
|
@ -6307,7 +6328,7 @@ body {
|
|||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
.docs-nav .navbar-nav > li .dropdown-menu > li.current > a.current {
|
||||
.docs-nav .navbar-nav > li.dropdown .dropdown-menu > li.current > a.current {
|
||||
background-color: #a24689;
|
||||
color: white;
|
||||
}
|
||||
|
@ -6709,6 +6730,74 @@ td.field-body > ul {
|
|||
.descclassname {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.index-tree ul,
|
||||
.index-tree li {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.index-tree > ul > li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.index-tree > ul > li > a {
|
||||
font-family: Lato, Arial, sans-serif;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.index-tree > ul > li > a small,
|
||||
.index-tree > ul > li > a .small {
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #777777;
|
||||
}
|
||||
.index-tree > ul > li > a small,
|
||||
.index-tree > ul > li > a .small {
|
||||
font-size: 65%;
|
||||
}
|
||||
.index-tree > ul > li > ul {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.index-tree > ul > li > ul > li {
|
||||
width: 50%;
|
||||
}
|
||||
.index-tree > ul > li > ul > li a {
|
||||
font-family: Lato, Arial, sans-serif;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 18px;
|
||||
padding-left: 10%;
|
||||
padding-right: 10%;
|
||||
}
|
||||
.index-tree > ul > li > ul > li a small,
|
||||
.index-tree > ul > li > ul > li a .small {
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #777777;
|
||||
}
|
||||
.index-tree > ul > li > ul > li a small,
|
||||
.index-tree > ul > li > ul > li a .small {
|
||||
font-size: 75%;
|
||||
}
|
||||
.index-tree > ul > li a {
|
||||
display: block;
|
||||
color: #a24689 !important;
|
||||
padding: 0.3em 0;
|
||||
margin: 3px !important;
|
||||
}
|
||||
.index-tree > ul > li a:hover {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
.stripe .section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,12 @@ body {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.document-super {
|
||||
.document-super:not(.stripe) {
|
||||
.container();
|
||||
}
|
||||
.document-super.stripe {
|
||||
.container-fluid();
|
||||
}
|
||||
|
||||
.document {
|
||||
.make-row();
|
||||
|
@ -89,20 +92,22 @@ body {
|
|||
opacity: 0;
|
||||
.transition(all 0.3s ease-out);
|
||||
}
|
||||
> a:after {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
border-top: 4px solid @gray-light;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
.dropdown-menu > li.current > a.current {
|
||||
background-color: @brand-primary;
|
||||
color: white;
|
||||
&.dropdown {
|
||||
> a:after {
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
border-top: 4px solid @gray-light;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
.dropdown-menu > li.current > a.current {
|
||||
background-color: @brand-primary;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* version switcher */
|
||||
|
@ -547,6 +552,44 @@ td.field-body {
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
|
||||
.index-tree {
|
||||
ul, li {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
> ul > li {
|
||||
margin-bottom: 0.5em;
|
||||
> a {
|
||||
.h3();
|
||||
text-align: center;
|
||||
}
|
||||
> ul {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
> li {
|
||||
width: 50%;
|
||||
a {
|
||||
.h4();
|
||||
padding-left: 10%;
|
||||
padding-right: 10%;
|
||||
}
|
||||
}
|
||||
}
|
||||
a {
|
||||
display: block;
|
||||
color: @link-color !important;
|
||||
padding: 0.3em 0;
|
||||
margin: 3px !important;
|
||||
&:hover {
|
||||
background-color: @gray-lighter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// STRIPE-STYLE PAGES
|
||||
.stripe {
|
||||
.section {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
:classes: stripe
|
||||
|
||||
===========
|
||||
Odoo as API
|
||||
Web Service
|
||||
===========
|
||||
|
||||
Odoo is mostly extended internally via modules, but much of its features and
|
||||
|
@ -250,8 +250,8 @@ the login.
|
|||
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.
|
||||
The second endpoint is ``xmlrpc/2/object``, is used to call methods of odoo
|
||||
models via the ``execute_kw`` RPC function.
|
||||
|
||||
Each call to ``execute_kw`` takes the following parameters:
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
|
@ -4,38 +4,17 @@ odoo developer documentation
|
|||
|
||||
Welcome to the Odoo developer documentation.
|
||||
|
||||
This documentation is incomplete and may contain errors, if you wish to
|
||||
contribute, every page should have a :guilabel:`View on Github` link:
|
||||
|
||||
.. image:: images/view-on-github.*
|
||||
:align: center
|
||||
|
||||
Through this link you can edit documents and submit changes for review using
|
||||
`github's web interface
|
||||
<https://help.github.com/articles/editing-files-in-your-repository/>`_.
|
||||
Contributions are welcome and appreciated.
|
||||
|
||||
.. todo:: what's the documentation's license?
|
||||
|
||||
The documentation is currently organized in four sections:
|
||||
|
||||
* :doc:`tutorials`, aimed at introducing the primary areas of developing Odoo
|
||||
modules
|
||||
* :doc:`reference`, which ought be the complete and canonical documentation
|
||||
for Odoo subsystems
|
||||
* :doc:`modules`, documenting useful specialized modules and integration
|
||||
methods (and currently empty)
|
||||
|
||||
.. hidden toctree w/o titlesonly otherwise the titlesonly "sticks" to
|
||||
in-document toctrees and we can't have a toctree showing both "sibling"
|
||||
pages and current document sections
|
||||
|
||||
.. rst-class:: index-tree
|
||||
.. titlesonly breaks level 3 (~in-document) toc of left navbar, so use
|
||||
maxdepth instead
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 2
|
||||
|
||||
tutorials
|
||||
reference
|
||||
modules
|
||||
api_integration
|
||||
|
||||
.. ifconfig:: todo_include_todos
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
==============
|
||||
Module Objects
|
||||
==============
|
||||
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
modules/api_integration
|
|
@ -71,7 +71,7 @@ Scaffolding is available via the :command:`odoo.py scaffold` subcommand.
|
|||
.. option:: -t <template>
|
||||
|
||||
a template directory, files are passed through jinja2_ then copied to
|
||||
the :option:`destination` directory
|
||||
the ``destination`` directory
|
||||
|
||||
.. option:: name
|
||||
|
||||
|
|
|
@ -421,10 +421,31 @@ added.
|
|||
is not written to the database, just used to know which value to send back
|
||||
to the client
|
||||
|
||||
Low-level SQL
|
||||
-------------
|
||||
|
||||
The :attr:`~openerp.api.Environment.cr` attribute on environments is the
|
||||
cursor for the current database transaction and allows executing SQL directly,
|
||||
either for queries which are difficult to express using the ORM (e.g. complex
|
||||
joins) or for performance reasons::
|
||||
|
||||
self.env.cr.execute("some_sql", param1, param2, param3)
|
||||
|
||||
Because models use the same cursor and the :class:`~openerp.api.Environment`
|
||||
holds various caches, these caches must be invalidated when *altering* the
|
||||
database in raw SQL, or further uses of models may become incoherent. It is
|
||||
necessary to clear caches when using ``CREATE``, ``UPDATE`` or ``DELETE`` in
|
||||
SQL, but not ``SELECT`` (which simply reads the database).
|
||||
|
||||
Clearing caches can be performed using the
|
||||
:meth:`~openerp.api.Environment.invalidate_all` method of the
|
||||
:class:`~openerp.api.Environment` object.
|
||||
|
||||
|
||||
.. _reference/orm/oldapi:
|
||||
|
||||
Old API compatibility
|
||||
---------------------
|
||||
=====================
|
||||
|
||||
Odoo is currently transitioning from an older (less regular) API, it can be
|
||||
necessary to manually bridge from one to the other manually:
|
||||
|
@ -508,170 +529,6 @@ return lists of ids, there is also a decorator managing this:
|
|||
>>> old_style_model.some_method(cr, uid, [1, 2, 3], context=context)
|
||||
[1, 2, 3]
|
||||
|
||||
Porting from the old API
|
||||
------------------------
|
||||
|
||||
* methods still written in the old API should be automatically bridged by the
|
||||
ORM, no need to switch to the old API, just call them as if they were a new
|
||||
API method. See :ref:`reference/orm/oldapi/bridging` for more details.
|
||||
* ``search`` returns a recordset, no point in e.g. browsing its result
|
||||
* ``fields.related`` and ``fields.function`` are replaced by using a normal
|
||||
field type with either a ``related`` or a ``compute`` parameter
|
||||
* ``depends`` on field compute methods **must be complete**, it must list
|
||||
**all** the fields and sub-fields which the compute method uses. It is
|
||||
better to have too many dependencies (will recompute the field in cases
|
||||
where that is not needed) than not enough (will forget to recompute the
|
||||
field and then values will be incorrect)
|
||||
* **remove** all ``onchange`` methods on computed fields. Computed fields are
|
||||
automatically re-computed when one of their dependencies is changed, and
|
||||
that is used to auto-generate ``onchange`` by the client
|
||||
* the decorators :func:`~openerp.api.model` and :func:`~openerp.api.multi` are
|
||||
for bridging *when calling from the old API context*, for internal or pure
|
||||
new-api (e.g. compute) they are useless
|
||||
* remove :attr:`~openerp.models.Model._default`, replace by ``default=``
|
||||
parameter on corresponding fields
|
||||
* if a field's ``string`` is the titlecased version of the field name::
|
||||
|
||||
name = fields.Char(string="Name")
|
||||
|
||||
it is useless and should be removed
|
||||
* ``multi`` does not do anything on new API fields use the same ``compute``
|
||||
methods on all relevant fields for the same result
|
||||
* provide ``compute``, ``inverse`` and ``search`` methods by name (as a
|
||||
string), this makes them overridable (removes the need for an intermediate
|
||||
"trampoline" function)
|
||||
* double check that all fields and methods have different names, there is no
|
||||
warning in case of collision (because Python handles it before Odoo sees
|
||||
anything)
|
||||
* the normal new-api import is ``from openerp import fields, models``. If
|
||||
compatibility decorators are necessary, use ``from openerp import api,
|
||||
fields, models``
|
||||
* avoid the :func:`~openerp.api.one` decorator, it probably does not do what
|
||||
you expect
|
||||
* remove explicit definition of :attr:`~openerp.models.Model.create_uid`,
|
||||
:attr:`~openerp.models.Model.create_date`,
|
||||
:attr:`~openerp.models.Model.write_uid` and
|
||||
:attr:`~openerp.models.Model.write_date` fields: they are now created as
|
||||
regular "legitimate" fields, and can be read and written like any other
|
||||
field out-of-the-box
|
||||
* when straight conversion is impossible (semantics can not be bridged) or the
|
||||
"old API" version is not desirable and could be improved for the new API, it
|
||||
is possible to use completely different "old API" and "new API"
|
||||
implementations for the same method name using :func:`~openerp.api.v7` and
|
||||
:func:`~openerp.api.v8`. The method should first be defined using the
|
||||
old-API style and decorated with :func:`~openerp.api.v7`, it should then be
|
||||
re-defined using the exact same name but the new-API style and decorated
|
||||
with :func:`~openerp.api.v8`. Calls from an old-API context will be
|
||||
dispatched to the first implementation and calls from a new-API context will
|
||||
be dispatched to the second implementation. One implementation can call (and
|
||||
frequently does) call the other by switching context.
|
||||
|
||||
.. danger:: using these decorators makes methods extremely difficult to
|
||||
override and harder to understand and document
|
||||
* uses of :attr:`~openerp.models.Model._columns` or
|
||||
:attr:`~openerp.models.Model._all_columns` should be replaced by
|
||||
:attr:`~openerp.models.Model._fields`, which provides access to instances of
|
||||
new-style :class:`openerp.fields.Field` instances (rather than old-style
|
||||
:class:`openerp.osv.fields._column`).
|
||||
|
||||
Non-stored computed fields created using the new API style are *not*
|
||||
available in :attr:`~openerp.models.Model._columns` and can only be
|
||||
inspected through :attr:`~openerp.models.Model._fields`
|
||||
* reassigning ``self`` in a method is probably unnecessary and may break
|
||||
translation introspection
|
||||
* :class:`~openerp.api.Environment` objects rely on some threadlocal state,
|
||||
which has to be set up before using them. It is necessary to do so using the
|
||||
:meth:`openerp.api.Environment.manage` context manager when trying to use
|
||||
the new API in contexts where it hasn't been set up yet, such as new threads
|
||||
or a Python interactive environment::
|
||||
|
||||
>>> from openerp import api, modules
|
||||
>>> r = modules.registry.RegistryManager.get('test')
|
||||
>>> cr = r.cursor()
|
||||
>>> env = api.Environment(cr, 1, {})
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: environments
|
||||
>>> with api.Environment.manage():
|
||||
... env = api.Environment(cr, 1, {})
|
||||
... print env['res.partner'].browse(1)
|
||||
...
|
||||
res.partner(1,)
|
||||
|
||||
.. _reference/orm/oldapi/bridging:
|
||||
|
||||
Automatic bridging of old API methods
|
||||
'''''''''''''''''''''''''''''''''''''
|
||||
|
||||
When models are initialized, all methods are automatically scanned and bridged
|
||||
if they look like models declared in the old API style. This bridging makes
|
||||
them transparently callable from new-API-style methods.
|
||||
|
||||
Methods are matched as "old-API style" if their second positional parameter
|
||||
(after ``self``) is called either ``cr`` or ``cursor``. The system also
|
||||
recognizes the third positional parameter being called ``uid`` or ``user`` and
|
||||
the fourth being called ``id`` or ``ids``. It also recognizes the presence of
|
||||
any parameter called ``context``.
|
||||
|
||||
When calling such methods from a new API context, the system will
|
||||
automatically fill matched parameters from the current
|
||||
:class:`~openerp.api.Environment` (for :attr:`~openerp.api.Environment.cr`,
|
||||
:attr:`~openerp.api.Environment.user` and
|
||||
:attr:`~openerp.api.Environment.context`) or the current recordset (for ``id``
|
||||
and ``ids``).
|
||||
|
||||
In the rare cases where it is necessary, the bridging can be customized by
|
||||
decorating the old-style method:
|
||||
|
||||
* disabling it entirely, by decorating a method with
|
||||
:func:`~openerp.api.noguess` there will be no bridging and methods will be
|
||||
called the exact same way from the new and old API styles
|
||||
* defining the bridge explicitly, this is mostly for methods which are matched
|
||||
incorrectly (because parameters are named in unexpected ways):
|
||||
|
||||
:func:`~openerp.api.cr`
|
||||
will automatically prepend the current cursor to explicitly provided
|
||||
parameters, positionally
|
||||
:func:`~openerp.api.cr_uid`
|
||||
will automatically prepend the current cursor and user's id to explictly
|
||||
provided parameters
|
||||
:func:`~openerp.api.cr_uid_ids`
|
||||
will automatically prepend the current cursor, user's id and recordset's
|
||||
ids to explicitly provided parameters
|
||||
:func:`~openerp.api.cr_uid_id`
|
||||
will loop over the current recordset and call the method once for each
|
||||
record, prepending the current cursor, user's id and record's id to
|
||||
explicitly provided parameters.
|
||||
|
||||
.. danger:: the result of this wrapper is *always a list* when calling
|
||||
from a new-API context
|
||||
|
||||
All of these methods have a ``_context``-suffixed version
|
||||
(e.g. :func:`~openerp.api.cr_uid_context`) which also passes the current
|
||||
context *by keyword*.
|
||||
* dual implementations using :func:`~openerp.api.v7` and
|
||||
:func:`~openerp.api.v8` will be ignored as they provide their own "bridging"
|
||||
|
||||
Low-level SQL
|
||||
-------------
|
||||
|
||||
The :attr:`~openerp.api.Environment.cr` attribute on environments is the
|
||||
cursor for the current database transaction and allows executing SQL directly,
|
||||
either for queries which are difficult to express using the ORM (e.g. complex
|
||||
joins) or for performance reasons::
|
||||
|
||||
self.env.cr.execute("some_sql", param1, param2, param3)
|
||||
|
||||
Because models use the same cursor and the :class:`~openerp.api.Environment`
|
||||
holds various caches, these caches must be invalidated when *altering* the
|
||||
database in raw SQL, or further uses of models may become incoherent. It is
|
||||
necessary to clear caches when using ``CREATE``, ``UPDATE`` or ``DELETE`` in
|
||||
SQL, but not ``SELECT`` (which simply reads the database).
|
||||
|
||||
Clearing caches can be performed using the
|
||||
:meth:`~openerp.api.Environment.invalidate_all` method of the
|
||||
:class:`~openerp.api.Environment` object.
|
||||
|
||||
.. _reference/orm/model:
|
||||
|
||||
Model Reference
|
||||
|
@ -1168,3 +1025,148 @@ Domain criteria can be combined using logical operators in *prefix* form:
|
|||
(name is 'ABC')
|
||||
AND (language is NOT english)
|
||||
AND (country is Belgium OR Germany)
|
||||
|
||||
Porting from the old API
|
||||
========================
|
||||
|
||||
* methods still written in the old API should be automatically bridged by the
|
||||
ORM, no need to switch to the old API, just call them as if they were a new
|
||||
API method. See :ref:`reference/orm/oldapi/bridging` for more details.
|
||||
* :meth:`~openerp.models.Model.search` returns a recordset, no point in e.g.
|
||||
browsing its result
|
||||
* ``fields.related`` and ``fields.function`` are replaced by using a normal
|
||||
field type with either a ``related=`` or a ``compute=`` parameter
|
||||
* :func:`~openerp.api.depends` on ``compute=`` methods **must be complete**,
|
||||
it must list **all** the fields and sub-fields which the compute method
|
||||
uses. It is better to have too many dependencies (will recompute the field
|
||||
in cases where that is not needed) than not enough (will forget to recompute
|
||||
the field and then values will be incorrect)
|
||||
* **remove** all ``onchange`` methods on computed fields. Computed fields are
|
||||
automatically re-computed when one of their dependencies is changed, and
|
||||
that is used to auto-generate ``onchange`` by the client
|
||||
* the decorators :func:`~openerp.api.model` and :func:`~openerp.api.multi` are
|
||||
for bridging *when calling from the old API context*, for internal or pure
|
||||
new-api (e.g. compute) they are useless
|
||||
* remove :attr:`~openerp.models.Model._default`, replace by ``default=``
|
||||
parameter on corresponding fields
|
||||
* if a field's ``string=`` is the titlecased version of the field name::
|
||||
|
||||
name = fields.Char(string="Name")
|
||||
|
||||
it is useless and should be removed
|
||||
* the ``multi=`` parameter does not do anything on new API fields use the same
|
||||
``compute=`` methods on all relevant fields for the same result
|
||||
* provide ``compute=``, ``inverse=`` and ``search=`` methods by name (as a
|
||||
string), this makes them overridable (removes the need for an intermediate
|
||||
"trampoline" function)
|
||||
* double check that all fields and methods have different names, there is no
|
||||
warning in case of collision (because Python handles it before Odoo sees
|
||||
anything)
|
||||
* the normal new-api import is ``from openerp import fields, models``. If
|
||||
compatibility decorators are necessary, use ``from openerp import api,
|
||||
fields, models``
|
||||
* avoid the :func:`~openerp.api.one` decorator, it probably does not do what
|
||||
you expect
|
||||
* remove explicit definition of :attr:`~openerp.models.Model.create_uid`,
|
||||
:attr:`~openerp.models.Model.create_date`,
|
||||
:attr:`~openerp.models.Model.write_uid` and
|
||||
:attr:`~openerp.models.Model.write_date` fields: they are now created as
|
||||
regular "legitimate" fields, and can be read and written like any other
|
||||
field out-of-the-box
|
||||
* when straight conversion is impossible (semantics can not be bridged) or the
|
||||
"old API" version is not desirable and could be improved for the new API, it
|
||||
is possible to use completely different "old API" and "new API"
|
||||
implementations for the same method name using :func:`~openerp.api.v7` and
|
||||
:func:`~openerp.api.v8`. The method should first be defined using the
|
||||
old-API style and decorated with :func:`~openerp.api.v7`, it should then be
|
||||
re-defined using the exact same name but the new-API style and decorated
|
||||
with :func:`~openerp.api.v8`. Calls from an old-API context will be
|
||||
dispatched to the first implementation and calls from a new-API context will
|
||||
be dispatched to the second implementation. One implementation can call (and
|
||||
frequently does) call the other by switching context.
|
||||
|
||||
.. danger:: using these decorators makes methods extremely difficult to
|
||||
override and harder to understand and document
|
||||
* uses of :attr:`~openerp.models.Model._columns` or
|
||||
:attr:`~openerp.models.Model._all_columns` should be replaced by
|
||||
:attr:`~openerp.models.Model._fields`, which provides access to instances of
|
||||
new-style :class:`openerp.fields.Field` instances (rather than old-style
|
||||
:class:`openerp.osv.fields._column`).
|
||||
|
||||
Non-stored computed fields created using the new API style are *not*
|
||||
available in :attr:`~openerp.models.Model._columns` and can only be
|
||||
inspected through :attr:`~openerp.models.Model._fields`
|
||||
* reassigning ``self`` in a method is probably unnecessary and may break
|
||||
translation introspection
|
||||
* :class:`~openerp.api.Environment` objects rely on some threadlocal state,
|
||||
which has to be set up before using them. It is necessary to do so using the
|
||||
:meth:`openerp.api.Environment.manage` context manager when trying to use
|
||||
the new API in contexts where it hasn't been set up yet, such as new threads
|
||||
or a Python interactive environment::
|
||||
|
||||
>>> from openerp import api, modules
|
||||
>>> r = modules.registry.RegistryManager.get('test')
|
||||
>>> cr = r.cursor()
|
||||
>>> env = api.Environment(cr, 1, {})
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: environments
|
||||
>>> with api.Environment.manage():
|
||||
... env = api.Environment(cr, 1, {})
|
||||
... print env['res.partner'].browse(1)
|
||||
...
|
||||
res.partner(1,)
|
||||
|
||||
.. _reference/orm/oldapi/bridging:
|
||||
|
||||
Automatic bridging of old API methods
|
||||
-------------------------------------
|
||||
|
||||
When models are initialized, all methods are automatically scanned and bridged
|
||||
if they look like models declared in the old API style. This bridging makes
|
||||
them transparently callable from new-API-style methods.
|
||||
|
||||
Methods are matched as "old-API style" if their second positional parameter
|
||||
(after ``self``) is called either ``cr`` or ``cursor``. The system also
|
||||
recognizes the third positional parameter being called ``uid`` or ``user`` and
|
||||
the fourth being called ``id`` or ``ids``. It also recognizes the presence of
|
||||
any parameter called ``context``.
|
||||
|
||||
When calling such methods from a new API context, the system will
|
||||
automatically fill matched parameters from the current
|
||||
:class:`~openerp.api.Environment` (for :attr:`~openerp.api.Environment.cr`,
|
||||
:attr:`~openerp.api.Environment.user` and
|
||||
:attr:`~openerp.api.Environment.context`) or the current recordset (for ``id``
|
||||
and ``ids``).
|
||||
|
||||
In the rare cases where it is necessary, the bridging can be customized by
|
||||
decorating the old-style method:
|
||||
|
||||
* disabling it entirely, by decorating a method with
|
||||
:func:`~openerp.api.noguess` there will be no bridging and methods will be
|
||||
called the exact same way from the new and old API styles
|
||||
* defining the bridge explicitly, this is mostly for methods which are matched
|
||||
incorrectly (because parameters are named in unexpected ways):
|
||||
|
||||
:func:`~openerp.api.cr`
|
||||
will automatically prepend the current cursor to explicitly provided
|
||||
parameters, positionally
|
||||
:func:`~openerp.api.cr_uid`
|
||||
will automatically prepend the current cursor and user's id to explictly
|
||||
provided parameters
|
||||
:func:`~openerp.api.cr_uid_ids`
|
||||
will automatically prepend the current cursor, user's id and recordset's
|
||||
ids to explicitly provided parameters
|
||||
:func:`~openerp.api.cr_uid_id`
|
||||
will loop over the current recordset and call the method once for each
|
||||
record, prepending the current cursor, user's id and record's id to
|
||||
explicitly provided parameters.
|
||||
|
||||
.. danger:: the result of this wrapper is *always a list* when calling
|
||||
from a new-API context
|
||||
|
||||
All of these methods have a ``_context``-suffixed version
|
||||
(e.g. :func:`~openerp.api.cr_uid_context`) which also passes the current
|
||||
context *by keyword*.
|
||||
* dual implementations using :func:`~openerp.api.v7` and
|
||||
:func:`~openerp.api.v8` will be ignored as they provide their own "bridging"
|
||||
|
|
Loading…
Reference in New Issue