Odoo Coding Standards & Code Inspection
This document details the Odoo Coding Standards enforced by the VS Code plugin's inspection tools. These standards are derived from Odoo's official coding guidelines and best practices, and are automatically checked by the linter to ensure code quality, maintainability, and consistency across all Odoo modules.
Why Coding Standards?
Adhering to Odoo's coding standards ensures:
- High code quality and maintainability
- Consistent naming and structure across modules
- Fewer bugs and easier onboarding for new developers
- Professional, production-ready Odoo solutions
The inspection system provides:
- Automatic enforcement of Odoo's Python, XML, and CSV standards
- 70+ validation rules covering naming, structure, and Odoo-specific patterns
- Real-time feedback and precise error reporting in your editor
Overview of Odoo Coding Standards
- Python Standards: File/class/method/field naming, forbidden patterns, import order, and model naming
- XML Standards: File naming, tag/attribute order, ID and name patterns, and view naming
- CSV Standards: Only
ir.model.access.csv
is validated (header, required columns, ID/name patterns) - Real-time Validation: Inline error highlighting with precise error location reporting
- Quick Fixes: Automated corrections for common issues

Python File Inspections (Odoo Standards)
File Naming and Structure Standards
- File Naming:
- Python files must be snake_case:
^[a-z_]+\.py$
- Correct:python
sale_order.py
- Incorrect:python
SaleOrder.py # Not snake_case saleOrder.py # Not snake_case
- Correct:
- Model class files: file name must match the snake_case of the class name (e.g.,
SaleOrder
→sale_order.py
)- Correct:python
# File: sale_order.py class SaleOrder(models.Model): _name = 'sale.order'
- Incorrect:python
# File: saleOrder.py class SaleOrder(models.Model): _name = 'sale.order' # File name does not match class name
- Correct:
- Report Python files: must end with
_report.py
- Correct:python
sale_report.py
- Incorrect:python
salereport.py
- Correct:
- Python files must be snake_case:
- Class Naming:
- Model classes must use CamelCase:
^[A-Z][a-zA-Z0-9]*$
- Correct:python
class SaleOrder(models.Model):
- Incorrect:python
class sale_order(models.Model): # Not CamelCase
- Correct:
- Model classes must use CamelCase:
- Model Class/File Match:
- For each model class, the file name must match the snake_case of the class name.
- Error:
Odoo standard: File name should be '<expected>.py' for class '<ClassName>'
Import Order
- Imports must be grouped and ordered:
- Python stdlib
- Odoo core (e.g.,
odoo
, but notodoo.addons
) - Odoo addons (e.g.,
odoo.addons
)
- Correct:python
import os import sys from odoo import api, fields, models from odoo.addons.base.models import ir_model
- Incorrect:python
from odoo.addons.base.models import ir_model from odoo import api, fields, models import os
- Error:
Odoo imports: Wrong import order. Should be: 1) Python stdlib, 2) Odoo core, 3) Odoo addons
Python Code Standards
Forbidden Patterns
- Do not use
.clone()
; usedict(my_dict)
orlist(old_list)
instead.- Incorrect:python
new_dict = old_dict.clone() # Forbidden
- Correct:python
new_dict = dict(old_dict)
- Incorrect:
- Never call
cr.commit()
unless you created your own cursor.- Incorrect:python
self.env.cr.commit() # Forbidden
- Incorrect:
- Do not format strings before translation: use
_("Text %s", value)
instead of_("Text %s" % value)
.- Incorrect:python
_('Hello %s' % name) # Forbidden
- Correct:python
_('Hello %s', name)
- Incorrect:
- Do not use concatenated strings inside translation functions: write the full string as a literal.
- Incorrect:python
_('Hello ' + name) # Forbidden
- Incorrect:
- Do not format after translation: include formatting inside the
_()
call.- Incorrect:python
_('Hello') % name # Forbidden
- Incorrect:
- Do not nest translation calls: field values are automatically translated.
- Incorrect:python
_('Name: %s' % _(self.name)) # Forbidden
- Incorrect:
- Use
if collection:
instead ofif len(collection) > 0:
.- Incorrect:python
if len(records) > 0: ...
- Correct:python
if records: ...
- Incorrect:
- Use
for key in my_dict:
instead offor key in my_dict.keys():
.- Incorrect:python
for key in my_dict.keys(): ...
- Correct:python
for key in my_dict: ...
- Incorrect:
.get('key', None)
is redundant; use.get('key')
instead.- Incorrect:python
value = my_dict.get('foo', None)
- Correct:python
value = my_dict.get('foo')
- Incorrect:
Field Naming
- Many2one: must end with
_id
(e.g.,partner_id = fields.Many2one(...)
)- Correct:python
partner_id = fields.Many2one('res.partner')
- Incorrect:python
partner = fields.Many2one('res.partner') # Should end with _id
- Correct:
- One2many/Many2many: must end with
_ids
(e.g.,line_ids = fields.One2many(...)
)- Correct:python
line_ids = fields.One2many('sale.order.line', 'order_id') tag_ids = fields.Many2many('sale.order.tag')
- Incorrect:python
lines = fields.One2many('sale.order.line', 'order_id') # Should end with _ids tags = fields.Many2many('sale.order.tag') # Should end with _ids
- Correct:
- Error:
Odoo naming: Many2One field '<name>' should have '_id' suffix
, etc.
Method Naming
- Compute:
_compute_<field_name>
- Search:
_search_<field_name>
- Default:
_default_<field_name>
- Selection:
_selection_<field_name>
- Onchange:
_onchange_<field_name>
- Constraint:
_check_<constraint_name>
- Action:
action_<action_name>
- If a method contains the type keyword (e.g., 'compute', 'search') but does not start with the correct prefix, it is flagged.
- Correct:python
def _compute_amount_total(self): ... def _onchange_partner_id(self): ... def _check_amount_total(self): ... def action_confirm(self): ...
- Incorrect:python
def compute_amount_total(self): # Should start with _compute_ ... def onchange_partner_id(self): # Should start with _onchange_ ... def check_amount_total(self): # Should start with _check_ ... def confirm(self): # Should start with action_ ...
- Error:
Odoo naming: Compute method '<name>' should follow pattern: _compute_<field_name>
Action Methods
- All
def action_...
methods must callself.ensure_one()
at the beginning. - Correct:python
def action_confirm(self): self.ensure_one() ...
- Incorrect:python
def action_confirm(self): ... # Missing self.ensure_one()
- Error:
Odoo standard: Action methods should call self.ensure_one() at the beginning
Model Naming
- Model names must use singular form (e.g.,
res.partner
, notres.partners
).- Correct:python
_name = 'res.partner'
- Incorrect:python
_name = 'res.partners' # Should be singular
- Correct:
- Transient model names should follow
<base_model>.<action>
and avoid 'wizard'.- Correct:python
_name = 'sale.order.import'
- Incorrect:python
_name = 'sale.order.wizard' # Should not use 'wizard'
- Correct:
- Error:
Odoo standard: Model name '<name>' should use singular form
XML File Inspections (Odoo Standards)
File Naming
- Views: must end with
_views.xml
,_templates.xml
, or_menus.xml
- Correct:
sale_order_views.xml
- Incorrect:
sale_order.xml
- Correct:
- Security: must be named
ir.model.access.csv
,*_groups.xml
, or*_security.xml
- Correct:
ir.model.access.csv
,sale_groups.xml
- Incorrect:
access.xml
- Correct:
- Report: must end with
_report_views.xml
,_reports.xml
, or_templates.xml
- Correct:
sale_report_views.xml
- Incorrect:
sale_report.xml
- Correct:
- Wizard: must end with
_views.xml
- Correct:
sale_wizard_views.xml
- Incorrect:
sale_wizard.xml
- Correct:
- Data: must end with
_data.xml
or_demo.xml
- Correct:
sale_data.xml
,sale_demo.xml
- Incorrect:
sale.xml
- Correct:
Tag and Attribute Standards
<record>
tags must have bothid
andmodel
attributes, withid
beforemodel
.- Correct:xml
<record id="sale_order_view_form" model="ir.ui.view"> ... </record>
- Incorrect:xml
<record model="ir.ui.view" id="sale_order_view_form"> ... </record>
- Correct:
<field>
tags: thename
attribute must be the first attribute.- Correct:xml
<field name="name" type="char">Order Name</field>
- Incorrect:xml
<field type="char" name="name">Order Name</field>
- Correct:
<data>
tags: usenoupdate="1"
on<odoo>
tag instead of<data>
if all records are not-updatable.- Correct:xml
<odoo noupdate="1"> <data> ... </data> </odoo>
- Incorrect:xml
<data noupdate="1"> ... </data>
- Correct:
- Do not use
<record model="ir.ui.menu">
; use<menuitem>
instead.- Correct:xml
<menuitem id="sale_order_menu" ... />
- Incorrect:xml
<record id="menu_sale_order" model="ir.ui.menu"> ... </record>
- Correct:
ID and Name Patterns
- Menu IDs: must match
^[a-z_]+_menu(_[a-z_]+)?$
- Correct:xml
<menuitem id="sale_order_menu" ... /> <menuitem id="sale_order_menu_main" ... />
- Incorrect:xml
<menuitem id="menu_sale_order" ... /> <menuitem id="saleOrderMenu" ... />
- Correct:
- View IDs: must match
^[a-z_]+_view_(form|list|kanban|search|tree|calendar|graph|pivot|activity)(_inherit)?$
- Correct:xml
<record id="sale_order_view_form" model="ir.ui.view"> ... </record>
- Incorrect:xml
<record id="view_sale_order_form" model="ir.ui.view"> ... </record>
- Correct:
- Action IDs: must match
^[a-z_]+_action(_[a-z_]+)?$
- Correct:xml
<record id="sale_order_action" model="ir.actions.act_window"> ... </record>
- Incorrect:xml
<record id="action_sale_order" model="ir.actions.act_window"> ... </record>
- Correct:
- Group IDs: must match
^[a-z_]+_group_[a-z_]+$
- Correct:xml
<record id="sale_group_manager" model="res.groups"> ... </record>
- Incorrect:xml
<record id="group_sale_manager" model="res.groups"> ... </record>
- Correct:
- Rule IDs: must match
^[a-z_]+_rule_[a-z_]+$
- Correct:xml
<record id="sale_order_rule_company" model="ir.rule"> ... </record>
- Incorrect:xml
<record id="rule_sale_order_company" model="ir.rule"> ... </record>
- Correct:
View Name Field
- In
<record model="ir.ui.view">
, the<field name="name">
value must:- Use dots instead of underscores
- Match:
^[a-z._]+\.view\.(form|list|kanban|search|tree|calendar|graph|pivot|activity)(\.[a-z._]+)?$
- Correct:xml
<field name="name">sale.order.view.form</field>
- Incorrect:xml
<field name="name">sale_order_view_form</field>
- For inherited views, the name must contain
.inherit.<details>
- Correct:xml
<field name="name">sale.order.view.form.inherit.partner</field>
- Incorrect:xml
<field name="name">sale.order.view.form.partner</field>
- Correct:
CSV File Inspections (Odoo Standards)
Access Control Files (ir.model.access.csv)
- Must have header + data (minimum 2 lines)
- Required columns:
id
,name
,model_id
- Access rule IDs: must be
access_<model_name>
- Access rule names: must be
access.<model.name>
- Handles quoted fields, escape sequences, and reports precise error locations
- Correct:csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_sale_order,access.sale.order,model_sale_order,base.group_user,1,1,1,1
- Incorrect:csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink sale_order_access,sale.order.access,model_sale_order,base.group_user,1,1,1,1 # Wrong ID and name pattern
Advanced Inspection Features
- Context-Aware Validation: Uses file path and content to determine module names and expected patterns
- Cross-Reference Tracking: Links related elements (menus↔actions, models↔views) for consistency
- Pattern Intelligence: Automatically derives expected names from file context
- Error Reporting: Exact line and character position for all errors, with contextual messages
- Real-time Feedback: Immediate validation as you type
Configuration Options
Inspection Severity Levels
- Error: Critical issues that must be fixed
- Warning: Important issues that should be addressed
- Info: Suggestions for improvement
- Disabled: Turn off specific inspections
File Type Coverage
- Python Files: All
.py
files in Odoo modules - XML Files: View definitions, security rules, data files
- CSV Files: Only
ir.model.access.csv
is validated
Best Practices
- Follow Naming Conventions: Consistent patterns improve code readability
- Use Proper Structure: Organize files and directories according to Odoo standards
- Validate Early: Catch issues during development, not deployment
- Enable All Inspections: Use comprehensive validation from the start
- Address Issues Immediately: Fix violations as they appear
- Team Consistency: Share inspection settings across the development team
- Regular Reviews: Periodically review and update inspection rules
Summary Statistics
- Total Standards: 70+ validation rules
- Python Coverage: File/class/method/field naming, forbidden patterns, import order, model naming
- XML Coverage: File naming, tag/attribute order, ID/name patterns, view naming
- CSV Coverage: Only
ir.model.access.csv
(header, required columns, ID/name patterns) - Advanced Features: Cross-reference tracking, context-aware validation, pattern matching
The inspection system provides comprehensive coverage of Odoo development standards, ensuring code quality and consistency across your entire project.