Use this file to discover all available pages before exploring further.
Well-structured code is easier to read, easier to debug, and easier for teammates to extend without side effects. These guidelines define the conventions Odoo uses across Python, XML, JavaScript, and SCSS — apply them to every new module and every new piece of development you contribute.
Stable versions: when modifying existing files in a stable branch, the original file’s style strictly supersedes these guidelines. Never reformat existing stable files to comply with these rules — it inflates diffs and disrupts git blame. Keep changes minimal.Development version (master): apply these guidelines to existing code only for lines you are already modifying, or when the file is undergoing a major overhaul. In that case, first make a dedicated [MOV] commit to restructure, then apply the feature changes.
Use only lowercase alphanumeric characters and underscores: [a-z0-9_].Use file permissions 644 for files and 755 for directories.The complete tree of a sample plant_nursery module illustrates all conventions:
Use the <record> notation for view and data declarations:
Put id before model.
For <field> tags, put name first, then the value (inline or in eval), then other attributes ordered by importance.
Group records by model; only break this rule when action/menu/view dependencies require a different order.
Use <data noupdate="1"> only for records that must not be updated on module upgrade. If the entire file is non-updatable, set noupdate="1" on <odoo> and omit <data>.
# not very goodcube = []for i in res: cube.append((i['id'], i['name']))# bettercube = [(i['id'], i['name']) for i in res]
Collections are booleans:
bool([]) is Falsebool([1]) is True# write this:if some_collection: ...# not this:if len(some_collection): ...
Iterate efficiently:
# creates a temporary listfor key in my_dict.keys(): ...# betterfor key in my_dict: ...# accessing key-value pairsfor key, value in my_dict.items(): ...
The ORM manages the transaction for every RPC call. Calling cr.commit() outside of explicitly created database cursors will cause partial commits, broken rollbacks, and test pollution.
# BAD: breaks transactional integritycr.commit()# ONLY acceptable if you created the cursor yourself:cr = db.cursor()try: res = do_work(cr) cr.commit()except Exception: cr.rollback() raisefinally: cr.close()
try: with self.env.cr.savepoint(): do_stuff()except SomeSpecificException: handle_error()
PostgreSQL slows down after 64 savepoints in a single transaction. If you use savepoints in a loop (e.g. processing records one by one), limit the batch size.
use strict; is recommended for all JavaScript files.
Use a linter (e.g. jshint).
Never include minified JavaScript libraries in the source.
Use PascalCase for class declarations.
Each component should live in its own file with a meaningful name (e.g. activity.js for activity widgets).
Static files follow the same model-based split as Python: one file per logical component, using the same naming conventions.See the Odoo JavaScript coding guidelines wiki for more detail.
Order properties from the “outside in”: position → layout (display, margin, padding, width, height) → border → background → font → decorative (filter, etc.).Place scoped SCSS variables and CSS variables at the very top of a block, followed by an empty line:
Use CSS custom properties only for contextual DOM adaptations, not as a global design system:
// Define with a fallback in the component.o_MyComponent { color: var(--MyComponent-color, #{$o-component-color});}// Override in a specific context.o_MyDashboard { --MyComponent-color: #{$o-dashboard-color};}
Avoid defining CSS variables on :root — use SCSS variables for global design tokens instead.