Code inspection
Get warnings in Python and XML files when your code departs from Odoo's coding guidelines. The checks run as you open, edit and save a file, and appear in the editor and the Problems panel.

What you can do
The checks follow Odoo's coding guidelines. Every finding is a Warning, shown on the line that triggers it. The checks cover .py and .xml files. They do not cover CSV files such as ir.model.access.csv.
Python: model files
These checks run on Python files in a models/ folder.
| Rule | Warning example | Flagged code |
|---|---|---|
File name matches the class. SaleOrder goes in sale_order.py. | File name should be 'sale_order.py' for class 'SaleOrder' | class SaleOrder(models.Model) in saleorder.py |
| Model classes use CamelCase. | Model class 'sale_order' should use CamelCase | class sale_order(models.Model): |
Model _name uses the singular. | Model name 'sale.orders' should use singular form | _name = 'sale.orders' |
Transient model _name follows <base_model>.<action>. | Transient model name should follow pattern '<base_model>.<action>' | _name = 'saleorderimport' in a TransientModel |
Python: all files
| Rule | Warning example | Flagged code |
|---|---|---|
Do not use .clone(). Use dict(my_dict) or list(old_list). | Don't use .clone() | new_dict = old_dict.clone() |
Do not call cr.commit() yourself. | NEVER call cr.commit() yourself unless you created your own cursor | self.env.cr.commit() |
| Do not format a string before translating it. | Don't format strings before translation | _('Hello %s' % name) |
Do not concatenate inside _(). Write the full string. | Don't use concatenated strings inside translation functions | _('Hello ' + name) |
| Do not format after translating. | Don't format after translation | _('Hello') % name |
Do not nest _() calls. | Don't nest translation calls | _('Name: %s' % _(self.name)) |
| Test a collection directly. | Use 'if collection:' instead of 'if len(collection) > 0:' | if len(records) > 0: |
| Iterate a dictionary directly. | Use 'for key in my_dict:' | for key in my_dict.keys(): |
Do not pass None as the default of .get(). | .get('key', None) is redundant | my_dict.get('foo', None) |
Many2one field names end in _id. | Many2One field 'partner' should have '_id' suffix | partner = fields.Many2one('res.partner') |
One2many and Many2many field names end in _ids. | One2Many field 'lines' should have '_ids' suffix | lines = fields.One2many('sale.order.line', 'order_id') |
action_ methods call self.ensure_one() near the top. | Action methods should call self.ensure_one() at the beginning | def action_confirm(self): without self.ensure_one() in the next few lines |
Imports are ordered: standard library, then odoo, then odoo.addons. | Wrong import order. Should be: 1) Python stdlib, 2) Odoo core, 3) Odoo addons | import os below from odoo import api |
Python: method names
A method whose name contains one of these keywords must start with the matching prefix.
| Keyword | Required prefix | Flagged | Accepted |
|---|---|---|---|
| compute | _compute_<field_name> | compute_amount_total | _compute_amount_total |
| search | _search_<field_name> | search_partner | _search_partner |
| default | _default_<field_name> | default_currency | _default_currency |
| selection | _selection_<field_name> | selection_state | _selection_state |
| onchange | _onchange_<field_name> | onchange_partner_id | _onchange_partner_id |
| check | _check_<constraint_name> | check_amount_total | _check_amount_total |
| action | action_<action_name> | do_action | action_confirm |
XML
| Rule | Warning example | Flagged code |
|---|---|---|
id comes before model on <record>. | Place 'id' attribute before 'model' in <record> | <record model="ir.ui.view" id="sale_order_view_form"> |
name is the first attribute of <field>. | The 'name' attribute should be the first in <field> | <field type="char" name="name"> |
Put noupdate="1" on <odoo>, not on <data>. | Use noupdate="1" on <odoo> tag instead of <data> | <data noupdate="1"> |
Use <menuitem>. | Use <menuitem> tag instead of <record model="ir.ui.menu"> | <record id="menu_sale" model="ir.ui.menu"> |
View record IDs follow <model>_view_<type>, with _inherit for inherited views. | View ID 'view_sale_order_form' should follow pattern: <model_name>_view_<view_type> | <record id="view_sale_order_form" model="ir.ui.view"> |
View names use dots: <model.name>.view.<type>. | View name 'sale_order_view_form' should use dots instead of underscores | <field name="name">sale_order_view_form</field> |
Inherited view names contain .inherit.<details>. | Inherited view name '...' should contain '.inherit.<details>' suffix | <field name="name">sale.order.view.form.partner</field> |
View types in IDs and names are form, list, kanban, search, calendar, graph, pivot and activity.
IDs that contain a keyword must match a pattern. IDs without the keyword are not checked.
| ID contains | Pattern | Example |
|---|---|---|
_menu | <model>_menu or <model>_menu_<detail> | sale_order_menu, sale_order_menu_main |
_action | <model>_action or <model>_action_<detail> | sale_order_action |
_group_ | <module>_group_<group_name> | sale_group_manager |
_rule_ | <model>_rule_<group> | sale_order_rule_company |
File names
The check uses the name of the folder that holds the file.
| Folder | File | Rule |
|---|---|---|
views/ | XML | Ends with _views.xml, _templates.xml or _menus.xml. |
security/ | XML | Ends with _groups.xml or _security.xml. |
report/ | XML | Ends with _report_views.xml, _reports.xml or _templates.xml. |
wizard/ | XML | Ends with _views.xml. |
data/ | XML | Ends with _data.xml or _demo.xml. |
controllers/ | Python | Snake case: lowercase letters and _. |
wizard/ | Python | Snake case: lowercase letters and _. |
report/ | Python | Ends with _report.py. |
Get started
The linter is off by default. To turn it on:
- Open Settings and search for
Enable Code Standard Warnings, or setcybrosys-assista-odoo-helper.enableCodeStandardWarningstotrue. - Open a Python or XML file in an Odoo module and edit it. Warnings appear in the editor and in the Problems panel (
Ctrl+Shift+M, orCmd+Shift+Mon macOS).
Turn the setting off to clear the warnings.
Tips and limits
- Guidelines change between Odoo releases. Check the current Odoo coding guidelines if a warning looks wrong for your version.
- The checks are a subset of the guidelines, not a full replacement for review. They match text on one line at a time, so a tag whose attributes span several lines is not checked.
- The checks are approximate and can flag valid code:
- The translation rule looks for
%inside_(), so it also flags the recommended_('Hello %s', name). - The method rule looks for the keyword anywhere in the name.
research_topics(containssearch) and_compute_default_price(containsdefault) are flagged. - The plural rule flags a model name whose last word ends in
s, such asx.status. - Elements inside a view's
archthat have anidattribute are tested against the view ID pattern.
- The translation rule looks for
- Model tools write view IDs such as
sale_order_form_viewand report files namedreport/<model>_pdf_report.xml. Both fail these rules. Rename them if you want a clean Problems panel. - You cannot turn off individual rules. The setting enables or disables all of them.