Skip to content

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
    • Model class files: file name must match the snake_case of the class name (e.g., SaleOrdersale_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
    • Report Python files: must end with _report.py
      • Correct:
        python
        sale_report.py
      • Incorrect:
        python
        salereport.py
  • 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
  • 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:
    1. Python stdlib
    2. Odoo core (e.g., odoo, but not odoo.addons)
    3. 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(); use dict(my_dict) or list(old_list) instead.
    • Incorrect:
      python
      new_dict = old_dict.clone()  # Forbidden
    • Correct:
      python
      new_dict = dict(old_dict)
  • Never call cr.commit() unless you created your own cursor.
    • Incorrect:
      python
      self.env.cr.commit()  # Forbidden
  • 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)
  • Do not use concatenated strings inside translation functions: write the full string as a literal.
    • Incorrect:
      python
      _('Hello ' + name)  # Forbidden
  • Do not format after translation: include formatting inside the _() call.
    • Incorrect:
      python
      _('Hello') % name  # Forbidden
  • Do not nest translation calls: field values are automatically translated.
    • Incorrect:
      python
      _('Name: %s' % _(self.name))  # Forbidden
  • Use if collection: instead of if len(collection) > 0:.
    • Incorrect:
      python
      if len(records) > 0:
          ...
    • Correct:
      python
      if records:
          ...
  • Use for key in my_dict: instead of for key in my_dict.keys():.
    • Incorrect:
      python
      for key in my_dict.keys():
          ...
    • Correct:
      python
      for key in my_dict:
          ...
  • .get('key', None) is redundant; use .get('key') instead.
    • Incorrect:
      python
      value = my_dict.get('foo', None)
    • Correct:
      python
      value = my_dict.get('foo')

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
  • 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
  • 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 call self.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, not res.partners).
    • Correct:
      python
      _name = 'res.partner'
    • Incorrect:
      python
      _name = 'res.partners'  # Should be singular
  • 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'
  • 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
  • Security: must be named ir.model.access.csv, *_groups.xml, or *_security.xml
    • Correct: ir.model.access.csv, sale_groups.xml
    • Incorrect: access.xml
  • Report: must end with _report_views.xml, _reports.xml, or _templates.xml
    • Correct: sale_report_views.xml
    • Incorrect: sale_report.xml
  • Wizard: must end with _views.xml
    • Correct: sale_wizard_views.xml
    • Incorrect: sale_wizard.xml
  • Data: must end with _data.xml or _demo.xml
    • Correct: sale_data.xml, sale_demo.xml
    • Incorrect: sale.xml

Tag and Attribute Standards

  • <record> tags must have both id and model attributes, with id before model.
    • 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>
  • <field> tags: the name 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>
  • <data> tags: use noupdate="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>
  • 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>

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" ... />
  • 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>
  • 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>
  • 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>
  • 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>

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>

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.