[IMP] doc: improve index, WS page
This commit is contained in:
Vendored
+3
-2
@@ -32,7 +32,7 @@
|
|||||||
<div class="sphinxsidebar">
|
<div class="sphinxsidebar">
|
||||||
<div class="sphinxsidebarwrapper">
|
<div class="sphinxsidebarwrapper">
|
||||||
{{ toctree(maxdepth=4, collapse=False, includehidden=True,
|
{{ toctree(maxdepth=4, collapse=False, includehidden=True,
|
||||||
main_navbar=False, titles_only=False) }}
|
navbar='side', titles_only=False) }}
|
||||||
{% if github_link %}
|
{% if github_link %}
|
||||||
<p><a href="{{ github_link(mode='edit') }}" class="github">
|
<p><a href="{{ github_link(mode='edit') }}" class="github">
|
||||||
Edit on GitHub
|
Edit on GitHub
|
||||||
@@ -72,7 +72,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<nav class="collapse navbar-collapse navbar-main" role="navigation">
|
<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>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
+19
-24
@@ -1,7 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import sphinx.roles
|
import sphinx.roles
|
||||||
import sphinx.environment
|
import sphinx.environment
|
||||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
|
||||||
from sphinx.writers.html import HTMLTranslator
|
from sphinx.writers.html import HTMLTranslator
|
||||||
from docutils.writers.html4css1 import HTMLTranslator as DocutilsTranslator
|
from docutils.writers.html4css1 import HTMLTranslator as DocutilsTranslator
|
||||||
|
|
||||||
@@ -9,27 +8,17 @@ def patch():
|
|||||||
# navify toctree (oh god)
|
# navify toctree (oh god)
|
||||||
@monkey(sphinx.environment.BuildEnvironment)
|
@monkey(sphinx.environment.BuildEnvironment)
|
||||||
def resolve_toctree(old_resolve, self, *args, **kwargs):
|
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)
|
toc = old_resolve(self, *args, **kwargs)
|
||||||
if toc is None:
|
if toc is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
navbarify(toc[0], main_navbar=main_navbar)
|
navbarify(toc[0], navbar=navbar)
|
||||||
return toc
|
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
|
# monkeypatch visit_table to remove border and add .table
|
||||||
HTMLTranslator.visit_table = visit_table
|
HTMLTranslator.visit_table = visit_table
|
||||||
# disable colspec crap
|
# disable colspec crap
|
||||||
@@ -37,11 +26,18 @@ def patch():
|
|||||||
# copy data- attributes straight from source to dest
|
# copy data- attributes straight from source to dest
|
||||||
HTMLTranslator.starttag = starttag_data
|
HTMLTranslator.starttag = starttag_data
|
||||||
|
|
||||||
def navbarify(node, main_navbar=False):
|
def navbarify(node, navbar=None):
|
||||||
# add classes to toplevel
|
"""
|
||||||
if not main_navbar:
|
:param node: toctree node to navbarify
|
||||||
navify([node])
|
:param navbar: Whether this toctree is a 'main' navbar, a 'side' navbar or
|
||||||
else:
|
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'])
|
node['classes'].extend(['nav', 'navbar-nav', 'navbar-right'])
|
||||||
for list_item in node.children:
|
for list_item in node.children:
|
||||||
# bullet_list
|
# bullet_list
|
||||||
@@ -52,6 +48,10 @@ def navbarify(node, main_navbar=False):
|
|||||||
# list_item
|
# list_item
|
||||||
# compact_paragraph
|
# compact_paragraph
|
||||||
# reference
|
# reference
|
||||||
|
# no bullet_list.list_item -> don't dropdownify
|
||||||
|
if not list_item.children[1].children:
|
||||||
|
return
|
||||||
|
|
||||||
list_item['classes'].append('dropdown')
|
list_item['classes'].append('dropdown')
|
||||||
# list_item.compact_paragraph.reference
|
# list_item.compact_paragraph.reference
|
||||||
link = list_item.children[0].children[0]
|
link = list_item.children[0].children[0]
|
||||||
@@ -59,11 +59,6 @@ def navbarify(node, main_navbar=False):
|
|||||||
link.attributes['data-toggle'] = 'dropdown'
|
link.attributes['data-toggle'] = 'dropdown'
|
||||||
# list_item.bullet_list
|
# list_item.bullet_list
|
||||||
list_item.children[1]['classes'].append('dropdown-menu')
|
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):
|
def visit_table(self, node):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Vendored
+102
-13
@@ -5902,8 +5902,10 @@ button.close {
|
|||||||
.panel-body:after,
|
.panel-body:after,
|
||||||
.modal-footer:before,
|
.modal-footer:before,
|
||||||
.modal-footer:after,
|
.modal-footer:after,
|
||||||
.document-super:before,
|
.document-super:not(.stripe):before,
|
||||||
.document-super:after,
|
.document-super:not(.stripe):after,
|
||||||
|
.document-super.stripe:before,
|
||||||
|
.document-super.stripe:after,
|
||||||
.document:before,
|
.document:before,
|
||||||
.document:after {
|
.document:after {
|
||||||
content: " ";
|
content: " ";
|
||||||
@@ -5924,7 +5926,8 @@ button.close {
|
|||||||
.pager:after,
|
.pager:after,
|
||||||
.panel-body:after,
|
.panel-body:after,
|
||||||
.modal-footer:after,
|
.modal-footer:after,
|
||||||
.document-super:after,
|
.document-super:not(.stripe):after,
|
||||||
|
.document-super.stripe:after,
|
||||||
.document:after {
|
.document:after {
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
@@ -6181,35 +6184,53 @@ body {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.document-super {
|
.document-super:not(.stripe) {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
}
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.document-super {
|
.document-super:not(.stripe) {
|
||||||
width: 750px;
|
width: 750px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
.document-super {
|
.document-super:not(.stripe) {
|
||||||
width: 970px;
|
width: 970px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
.document-super {
|
.document-super:not(.stripe) {
|
||||||
width: 1170px;
|
width: 1170px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.document-super > .navbar-header,
|
.document-super:not(.stripe) > .navbar-header,
|
||||||
.document-super > .navbar-collapse {
|
.document-super:not(.stripe) > .navbar-collapse {
|
||||||
margin-right: -15px;
|
margin-right: -15px;
|
||||||
margin-left: -15px;
|
margin-left: -15px;
|
||||||
}
|
}
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.document-super > .navbar-header,
|
.document-super:not(.stripe) > .navbar-header,
|
||||||
.document-super > .navbar-collapse {
|
.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-right: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
@@ -6296,7 +6317,7 @@ body {
|
|||||||
-o-transition: all 0.3s ease-out;
|
-o-transition: all 0.3s ease-out;
|
||||||
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: " ";
|
content: " ";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 0;
|
width: 0;
|
||||||
@@ -6307,7 +6328,7 @@ body {
|
|||||||
border-right: 4px solid transparent;
|
border-right: 4px solid transparent;
|
||||||
border-left: 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;
|
background-color: #a24689;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
@@ -6709,6 +6730,74 @@ td.field-body > ul {
|
|||||||
.descclassname {
|
.descclassname {
|
||||||
opacity: 0.5;
|
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 {
|
.stripe .section {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-15
@@ -25,9 +25,12 @@ body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-super {
|
.document-super:not(.stripe) {
|
||||||
.container();
|
.container();
|
||||||
}
|
}
|
||||||
|
.document-super.stripe {
|
||||||
|
.container-fluid();
|
||||||
|
}
|
||||||
|
|
||||||
.document {
|
.document {
|
||||||
.make-row();
|
.make-row();
|
||||||
@@ -89,20 +92,22 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
.transition(all 0.3s ease-out);
|
.transition(all 0.3s ease-out);
|
||||||
}
|
}
|
||||||
> a:after {
|
&.dropdown {
|
||||||
content: " ";
|
> a:after {
|
||||||
display: inline-block;
|
content: " ";
|
||||||
width: 0;
|
display: inline-block;
|
||||||
height: 0;
|
width: 0;
|
||||||
margin-left: 5px;
|
height: 0;
|
||||||
vertical-align: middle;
|
margin-left: 5px;
|
||||||
border-top: 4px solid @gray-light;
|
vertical-align: middle;
|
||||||
border-right: 4px solid transparent;
|
border-top: 4px solid @gray-light;
|
||||||
border-left: 4px solid transparent;
|
border-right: 4px solid transparent;
|
||||||
}
|
border-left: 4px solid transparent;
|
||||||
.dropdown-menu > li.current > a.current {
|
}
|
||||||
background-color: @brand-primary;
|
.dropdown-menu > li.current > a.current {
|
||||||
color: white;
|
background-color: @brand-primary;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* version switcher */
|
/* version switcher */
|
||||||
@@ -547,6 +552,44 @@ td.field-body {
|
|||||||
opacity: 0.5;
|
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-STYLE PAGES
|
||||||
.stripe {
|
.stripe {
|
||||||
.section {
|
.section {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
:classes: stripe
|
:classes: stripe
|
||||||
|
|
||||||
===========
|
===========
|
||||||
Odoo as API
|
Web Service
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Odoo is mostly extended internally via modules, but much of its features and
|
Odoo is mostly extended internally via modules, but much of its features and
|
||||||
@@ -250,8 +250,8 @@ the login.
|
|||||||
Calling methods
|
Calling methods
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The second — and most generally useful — is ``xmlrpc/2/object`` which is used
|
The second endpoint is ``xmlrpc/2/object``, is used to call methods of odoo
|
||||||
to call methods of odoo models via the ``execute_kw`` RPC function.
|
models via the ``execute_kw`` RPC function.
|
||||||
|
|
||||||
Each call to ``execute_kw`` takes the following parameters:
|
Each call to ``execute_kw`` takes the following parameters:
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
+5
-26
@@ -4,38 +4,17 @@ odoo developer documentation
|
|||||||
|
|
||||||
Welcome to the 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?
|
.. todo:: what's the documentation's license?
|
||||||
|
|
||||||
The documentation is currently organized in four sections:
|
.. rst-class:: index-tree
|
||||||
|
.. titlesonly breaks level 3 (~in-document) toc of left navbar, so use
|
||||||
* :doc:`tutorials`, aimed at introducing the primary areas of developing Odoo
|
maxdepth instead
|
||||||
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
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:maxdepth: 2
|
||||||
|
|
||||||
tutorials
|
tutorials
|
||||||
reference
|
reference
|
||||||
modules
|
api_integration
|
||||||
|
|
||||||
.. ifconfig:: todo_include_todos
|
.. 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>
|
.. option:: -t <template>
|
||||||
|
|
||||||
a template directory, files are passed through jinja2_ then copied to
|
a template directory, files are passed through jinja2_ then copied to
|
||||||
the :option:`destination` directory
|
the ``destination`` directory
|
||||||
|
|
||||||
.. option:: name
|
.. option:: name
|
||||||
|
|
||||||
|
|||||||
+167
-165
@@ -421,10 +421,31 @@ added.
|
|||||||
is not written to the database, just used to know which value to send back
|
is not written to the database, just used to know which value to send back
|
||||||
to the client
|
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:
|
.. _reference/orm/oldapi:
|
||||||
|
|
||||||
Old API compatibility
|
Old API compatibility
|
||||||
---------------------
|
=====================
|
||||||
|
|
||||||
Odoo is currently transitioning from an older (less regular) API, it can be
|
Odoo is currently transitioning from an older (less regular) API, it can be
|
||||||
necessary to manually bridge from one to the other manually:
|
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)
|
>>> old_style_model.some_method(cr, uid, [1, 2, 3], context=context)
|
||||||
[1, 2, 3]
|
[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:
|
.. _reference/orm/model:
|
||||||
|
|
||||||
Model Reference
|
Model Reference
|
||||||
@@ -1168,3 +1025,148 @@ Domain criteria can be combined using logical operators in *prefix* form:
|
|||||||
(name is 'ABC')
|
(name is 'ABC')
|
||||||
AND (language is NOT english)
|
AND (language is NOT english)
|
||||||
AND (country is Belgium OR Germany)
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user