This page introduce the new Odoo Coding Guidelines. This guidelines aims to improve the quality of the code (better readability of source, ...) but also to improve Odoo Apps ! Indeed, a proper code will ease the maintenance and debugging, lower the complexity and promote the reliability.
A module is organised in a few directory :
For views declarations, split backend views from (frontend) templates in 2 differents files.
For models, split the business logic by sets of models, in each sets select a main model, this model gives its name to the set. If there is only one set of module, its name is the same as the module name. For each set named <main_model> the following files may be created:
For instance, sale module introduce sale_order and sale_order_line where sale_order is dominant. So the <main_model> files will be named models/sale_order.py and views/sale_order_views.py.
For data, split them by purpose : demo or data. The filename will be the main_model name, suffixed by _demo.xml or _data.xml.
For controller, the only file should be named main.py.
For static files, the name pattern is <module_name>.ext (i.e. : static/js/im_chat.js, static/css/im_chat.css, static/xml/im_chat.xml, ...). Don’t link data (image, libraries) outside Odoo : don’t use an url to an image but copy it in our codebase instead.
The complete tree should looks like
addons/<my_module_name>/
|-- __init__.py
|-- __openerp__.py
|-- controllers/
| |-- __init__.py
| `-- main.py
|-- data/
| |-- <main_model>_data.xml
| `-- <inherited_main_model>_demo.xml
|-- models/
| |-- __init__.py
| |-- <main_model>.py
| `-- <inherited_main_model>.py
|-- security/
| |-- ir.model.access.csv
| `-- <main_model>_security.xml
|-- static/
| |-- img/
| | |-- my_little_kitten.png
| | `-- troll.jpg
| |-- lib/
| | `-- external_lib/
| `-- src/
| |-- js/
| | `-- <my_module_name>.js
| |-- css/
| | `-- <my_module_name>.css
| |-- less/
| | `-- <my_module_name>.less
| `-- xml/
| `-- <my_module_name>.xml
`-- views/
|-- <main_model>_templates.xml
|-- <main_model>_views.xml
|-- <inherited_main_model>_templates.xml
`-- <inherited_main_model>_views.xml
Note
Filename should only use only [a-z0-9_]
Warning
Use correct file permissions : folder 755 and file 644.
When declaring a record in XML,
<record id="view_id" model="ir.ui.view">
<field name="name">view.name</field>
<field name="model">object_name</field>
<field name="priority" eval="16"/>
<field name="arch" type="xml">
<tree>
<field name="my_field_1"/>
<field name="my_field_2" string="My Label" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}' />
</tree>
</field>
</record>
Use the following pattern :
<!-- views and menus -->
<record id="model_name_menu" model="ir.ui.menu">
...
</record>
<record id="model_name_view_form" model="ir.ui.view">
...
</record>
<record id="model_name_view_kanban" model="ir.ui.view">
...
</record>
<!-- actions -->
<record id="model_name_action" model="ir.actions.act_window">
...
</record>
<record id="model_name_action_child_list" model="ir.actions.act_window">
...
</record>
<!-- security -->
<record id="model_name_group_user" model="res.groups">
...
</record>
<record id="model_name_rule_public" model="ir.rule">
...
</record>
<record id="model_name_rule_company" model="ir.rule">
...
</record>
Note
View name use dot notation my.model.view_type or my.model.view_type.inherit instead of “This is the form view of My Model”.
The naming pattern of inherited view is <base_view>_inherit_<current_module_name>. A module can extend a view only one time, suffix the orginal name with _inherit_<current_module_name>, where current_module_name is the technical name of the module extending the view.
<record id="inherited_model_view_form_inherit_my_module" model="ir.ui.view">
...
</record>
Using a linter can help to see syntax and semantic warning or error. Odoo Source Code try to respect Python standard, but some of them can be ignored.
The imports are ordered as
Inside these 3 groups, the imported lines are alphabetically sorted.
# 1 : imports of python lib
import base64
import re
import time
# 2 : imports of openerp
import openerp
from openerp import api, fields, models # alphabetically ordered
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
# 3 : imports from odoo modules
from openerp.addons.website.models.website import slug
from openerp.addons.web.controllers.main import login_redirect
class AccountInvoice(models.Model):
...
class account_invoice(osv.osv):
...
ResPartner = self.env['res.partner']
partners = ResPartner.browse(ids)
partner_id = partners[0].id
One2Many and Many2Many fields should always have _ids as suffix (example: sale_order_line_ids)
Many2One fields should have _id as suffix (example : partner_id, user_id, ...)
class Event(models.Model):
# Private attributes
_name = 'event.event'
_description = 'Event'
# Default methods
def _default_name(self):
...
# Fields declaration
name = fields.Char(string='Name', default=_default_name)
seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats',
store=True, readonly=True, compute='_compute_seats')
seats_available = fields.Integer(oldname='register_avail', string='Available Seats',
store=True, readonly=True, compute='_compute_seats')
price = fields.Integer(string='Price')
# compute and search fields, in the same order that fields declaration
@api.multi
@api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register')
def _compute_seats(self):
...
# Constraints and onchanges
@api.constrains('seats_max', 'seats_available')
def _check_seats_limit(self):
...
@api.onchange('date_begin')
def _onchange_date_begin(self):
...
# CRUD methods
def create(self):
...
# Action methods
@api.multi
def action_validate(self):
self.ensure_one()
...
# Business methods
def mail_user_confirm(self):
...
For javascript :
For CSS :
Prefix your commit with
Then, in the message itself, specify the part of the code impacted by your changes (module name, lib, transversal object, ...) and a description of the changes.
[FIX] website, website_mail: remove unused alert div, fixes look of input-group-btn
Bootstrap's CSS depends on the input-group-btn
element being the first/last child of its parent.
This was not the case because of the invisible
and useless alert.
[IMP] fields: reduce memory footprint of list/set field attributes
[REF] web: add module system to the web client
This commit introduces a new module system for the javascript code.
Instead of using global ...
Note
The long description try to explain the why not the what, the what can be seen in the diff
odoo docs theme based on Bootstrap 3.2 documentation adapted to Sphinx output with branding and color changes.