Enable Dark Mode!
coding-guidelines-in-odoo-erp.jpg
By: Noorjahan NA

Coding Guidelines in Odoo ERP

Technical Odoo 16

Any programming language's coding standards give the code written by any developer a consistent appearance. It decreases internal complexity while making the code easier to read and maintain.

In this blog, we are going to explain the Odoo Coding guidelines. These seek to raise the standard of the code for Odoo Apps. Correct coding does, in fact, increase readability, make maintenance more straightforward, aid in debugging, reduce complexity, and increase reliability. Every new module and all new development should follow these rules.

Module Structure

Directories

Modules in Odoo have a predefined structure. Files of various categories must be kept in separate directories. Consequently, an Odoo module is organized into various directories. Those contain the business logic; looking at them should help you understand the module's purpose. 

1. controllers: includes the HTTP routes (python files).

2. data:  It contains the model data (XML file).

3. demo: Contains the module's demo data (load if only the 'load demo data' is enabled).

4. doc: Contains the module documentation. 

5. I18n: Contains the Translations.

6. models: It contains all models' definitions and functions(python files)

7. report: Contains all report-related files(both python and XML files)

8. security: Contains files that specify record rules and CSV files for user permission.

9. static: contains the module's web assets as well as subfolders for description, css/, js/, img/, lib/, etc.

10. tests: contains the files for the Python tests.

11. views: contains the definitions for the views and templates.

12. wizard:  contains the transient models and their views

14. __init__.py: To import the python directories (models, controllers, reports, ..,)

15. __manifest__.py: Core file of a module.

Not all directories are required. Depending on the module's purpose, it might include the following. However, the outer "__init .py" and "__manifest .py" files are necessary in order to create a module. Place an empty outer "__init .py" file even if the module's accompanying Python files are absent. 

File Naming

In order to rapidly find information across all Odoo addons, file naming is crucial. This section covers how to name files in a normal Odoo module. We will use a school management application as an illustration. The school. class and school. students models are its two core models. As with models, divide the business logic into groups of models that are subsets of the same basic model. Each set is contained in a specific file with a name derived from its primary model. The model's name is the same as the module name if there is just one model. To make it easier to comprehend how the inherited models are affected, each one should be in its own file. 

addons/school_management/
|-- models/
|   |-- school_class.py (first main model)
|   |-- school_students.py (another main model)
|   |-- res_partner.py (inherited Odoo model)

While considering the security, three main files are included. 

a) The first is an ir.model.access.csv file that defines access rights.

b) <module>_groups.xml contains definitions for user groups.

c) In <model>_security.xml, record rules are specified.

addons/school_management/
|-- security/
|   |-- ir.model.access.csv
|   |-- school_management_groups.xml
|   |-- school_class_security.xml
|   |-- school_students_security.xml

Backend views should be divided like models and prefixed with _views.xml when it comes to views. Templates (QWeb pages used notably for portal/website display) are specified as <model>_templates.xml.

Considering the data files, filenames will be the main model name suffixed by the _demo.xml and _data.xml. In terms of controllers, typically, every controller is a part of a single controller found in the file <module_name>.py. Odoo has a long-standing tradition of calling this file main.py, but this pattern is no longer followed.

If we need to inherit an existing controller from another module,  it can be done in <inherited_module_name>.py. For instance, using portal.py, a portal controller can be added to an application. While considering the static files, the Javascript files follow the same logic as the python models. Each component needs to be in its own file with a descriptive name. Additionally, subdirectories can be made to organize the "package". The same reasoning ought to be used for the styles(scss files) and static XML file templates of JS widgets. Don't link data (images, libraries) outside of Odoo: copy the data instead of using the URL to an image. The files in wizards are also named like the python models: <transient>.py and <transient>_views.xml. When it comes to Qweb reports, they may be divided into many categories, such as paper format and report actions. 

An overview of the module structure and file names are as follows:-

addons/school_management/

|-- __init__.py
|-- __manifest__.py
|-- controllers/
|   |-- __init__.py
|   |-- school.py
|   |-- portal.py
|-- data/
|   |-- school_class_data.xml
|   |-- school_class_demo.xml
|   |-- mail_data.xml
|-- models/
|   |-- __init__.py
|   |-- school_class.py
|   |-- school_students.py
|   |-- res_partner.py
|-- report/
|   |-- __init__.py
|   |-- school_students_report.py
|   |-- school_students_views.xml
|   |-- school_students_reports.xml (report actions, paperformat, ...)
|   |-- school_students_templates.xml (xml report templates)
|-- security/
|   |-- ir.model.access.csv
|   |-- school_management_groups.xml
|   |-- school_class_security.xml
|   |-- school_students_security.xml
|-- static/
|   |-- img/
|   |   |-- my_little_kitten.png
|   |   |-- troll.jpg
|   |-- lib/
|   |   |-- external_lib/
|   |-- src/
|   |   |-- js/
|   |   |   |-- widget_a.js
|   |   |   |-- widget_b.js
|   |   |-- scss/
|   |   |   |-- widget_a.scss
|   |   |   |-- widget_b.scss
|   |   |-- xml/
|   |   |   |-- widget_a.xml
|   |   |   |-- widget_a.xml
|-- views/
|   |-- assets.xml
|   |-- school_management_menus.xml
|   |-- school_class_views.xml
|   |-- school_class_templates.xml
|   |-- school_students_views.xml
|   |-- shool_students_templates.xml
|   |-- res_partner_views.xml
|-- wizard/
|   |--make_school_student.py
|   |--make_school_student_views.xml

The core file of an Odoo module is the __manifest .py file. It contains all information like a dictionary, including the name, version, author, dependencies, data, qweb, license, etc.

The 'data' key should be assigned to all view files in the module, and the 'qweb' key should include a list of all qweb views. Try to avoid empty keys while creating the manifest file. When adding a module's version number, follow Odoo's major version (16.0,15.0, etc.) with the module version numbers (1.0.0). 

XML Files

There are many conventions to take into account while declaring a view or record in XML including the following:-

a) The record notation (using record>) should be used for all view records. 

b) Place the id attribute before the model

c) Only non-updatable data with noupdate=1 is set using the tag "data." The noupdate=1 attribute can be placed on the <odoo> tag, and there need not be a <data> tag if the file contains exclusively non-updatable data. 

d) The name attribute comes first in the field declaration. The value should then be entered either in the field tag or using the eval attribute, followed by additional attributes (widget, options, etc.) in ascending order of significance. 

Odoo supports custom tags that serve as syntactic sugar: 

a) menu item: use this to quickly declare ir.ui.menu

b) template: to declare a QWeb View that only needs the view's arch section.

These tags are preferred over the record notation.

XML IDs and naming

Security, View, and Action

In Odoo, we use the following patterns:-

a) For the menu: <model_name>_menu and <model_name>_menu_do_stuff for the submenus

b) <model_name>_view_<view_type> is the format for views, where view_type is a tree, kanban, form, search, etc.

c) The main action respects the <model_name> action for an action. Others are suffixed with _<detail>, where detail is a lowercase string that briefly describes the action. This is only used if the model has numerous actions declared.

d) For window actions, suffix the name of the action by the specific view information like <model_name>_action_view_<view_type>.

e) When considering a group <module_name>_group_<group_name> will be the format. Where a group_name is the name of the group like a user, manager, etc.

f) For a rule, use the format: "<model_name>_rule_<concerned_group>," where the concerned group is the short name of the concerned group (e.g., "user" for the "model name group user," "public" for users who may see the rule publicly, "company" for rules that apply on multi-company, etc.).

Dots should be used in place of underscores in the name to match the XML id. Action names should be real because they are displayed.

<!-- views  -->
<record id="model_name_view_form" model="ir.ui.view">
    <field name="name">model.name.view.form</field>
    ...
</record>
<record id="model_name_view_kanban" model="ir.ui.view">
    <field name="name">model.name.view.kanban</field>
    ...
</record>
<!-- actions --->
<record id="model_name_action" model="ir.act.window">
    <field name="name">Model Main Action</field>
    ...
</record>
<record id="model_name_action_child_list" model="ir.act.act_window">
     <field name="name">Model Access Children</field>
     ....
</record>
<!-- menus and sub-menus ?
<menuitem id="model_name_menu_root" name="Main Menu" sequence="5"/>
<menuitem
    id="model_name_menu_action"
    name="Sub Menu 1"
    parent="module_name.module_name_menu_root"
    action="model_name_action"
    sequence="10"
/>
<!-- security -->
<record id="module_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>

Inheriting XML

The original record's ID should be used in the XML identifiers of inheriting views. It facilitates quickly locating every inheritance. There is no overlap because final XML Ids are prefixed by the module that generates them.

A .inherit.{details} suffix should be included in the name to make it easier to grasp the overriding goal from the name alone. 

<record id="model_view_form" model="ir.ui.view">
    <field name="name">model.view.form.inherit.module2</field>
    <field name="inherit_id" ref="module1.model_view_form"/>
    ...
</record>

Since new main views are new records that are based on existing records, the inherit suffix is unnecessary.

<record id="module2.model_view_form" model="ir.ui.view">
    <field name="name">model.view.form.module2</field>
    <field name="inherit_id" ref="module1.model_view_form"/>
    <field name="mode">primary</field>
    ...
</record>

Python

PEP8 Options

Using a linter can help show syntax and semantic warnings or errors. Odoo source code tries to respect Python standards, but some of them can be ignored. 

a) E501: line too long

b) E301: expected 1 blank line, found 0

c) E302: expected 2 blank lines, found 1

Imports

The imports are arranged as follows:

1. External libraries (one per line sorted and split in python stdlib)

2. Imports of odoo

3. Imports from Odoo modules (rarely, and only if necessary)

Inside these three groups, the imported lines are alphabetically sorted.

# 1 : imports of python lib
import base64
import re
import time
from datetime import datetime
# 2 : imports of odoo
import odoo
from odoo import api, fields, models, _ # alphabetically ordered
from odoo.tools.safe_eval import safe_eval as eval
# 3 : imports from odoo addons
from odoo.addons.website.models.website import slug
from odoo.addons.web.controllers.main import login_redirect

Idiomatic of Programming (Python)

1) Always prioritize readability over concision or the use of idioms or language qualities.

2) Avoid using. clone ()

# bad
new_dict = my_dict.clone()
new_list = old_list.clone()
# good
new_dict = dict(my_dict)
new_list = list(old_list)

a) Use descriptive variable, class, and method names.

b) Temporary variables help clarify the code by providing things names, but that doesn't mean you should always create temporary variables.

c) When they are simpler, several return points are acceptable. 

d) Use list comprehension, dict comprehension, and basic manipulations using map, filter, and sum. It makes the code easier to read.

e) As a good practice, document your code (docstring on methods, comments on code, etc.)

Symbols and Conventions in Odoo

a) Use filtered, mapped, sorted, etc. methods, just like in Python, to make the code easier to comprehend and perform. 

b) Use only the generators and decorators offered by the Odoo API and refrain from creating your own.

Model name (using the dot notation, prefixed by the module name) :

c) Use the singular form of the name when naming an odoo model. For e.g.:- school.student instead of school.students

d) Use <related_base_model>.<action> when defining a transient model (wizard). Where the related_base_model is the base model related to the wizard, and action is the short name of what the transient is to do. Avoid the use of the word wizard. Eg:- account. move. make

e) Use camelcase when naming a Python class. 

Eg:- class SchoolStudent(models.Model)

Variable Naming

a) When naming a variable, use camel case

b) For common variables, use lowercase underscore notation.

c) If your variable contains a record id or set of record ids, prefix its name with _id or _ids. Avoid storing a record of res. partner in partner id. 

d) Always end One2Many and Many2Many fields with _ids. E.g.:- student_ids

e) The suffix _id should be used for Many2One fields. Eg:- student_id, user_id, etc.

Order of Attributes in a model

f) Private attributes Eg:- _name, _description, _inherit, etc.

g) Default methods and default_get method

h) Declaration of fields

i) search, inverse, and compute methods

j) Methods used to return the values for selection fields

k) Onchange methods and constraints method

l) CRUD methods

m) Action methods

n) All the other methods needed in the model

Method conventions

1. Compute field - the pattern of compute method is _compute_<field_name>

2. Search method - the pattern is _search_<field_name>

3. Constraint method - the pattern of the constraint method is as follows:- _check_<constraint_name>

4. Onchange method - the on-change method pattern is _onchange_<field_name>

5. Action method: Action_ is the prefix for an object's action method. . Since it uses only one record, add self.ensure_one() at the beginning of the method.

Javascript and CSS

Static files organization

Odoo extensions follow specific file structure guidelines. Here, we go into greater detail on how web assets should be arranged. The first thing to understand is that all files in the static/ folder that are prefixed with the addon name will be served (statically) by the Odoo server. 

As an illustration, if a file is placed at addons/web/static/src/js/some file.js, it will be statically accessible at the address your-odoo-server.com/web/static/src/js/some file.js. 

The following is the structure to organize the code:-

static: all static files in general

¦ static/lib: it contains the js libraries in a subfolder. Eg:- all the files from the jquery library are located in addons/web/static/lib/jquery. 

¦ static/src: It is the generic static source folder

¦ static/src/css: contains all css files

¦ static/fonts

¦ static/img

¦ static/src/js

¦ static/src/js/tours: end-user tour files (tutorials, not tests)

¦ static/src/scss: scss files

¦ static/src/xml: all qweb templates that will be rendered in JS

¦ static/tests: this is where we put all test-related files.

¦ static/tests/tours: this is where we put all tour test files (not tutorials).

Javascript Coding Guidelines

a) Use the ES6 modules for Odoo in the new code.

b) Never add minified Javascript Libraries

c) Use camelcase for class declaration

d) Avoid introspection whenever you can by not creating a method name dynamically. It is more delicate and challenging to refactor.

e) Write unit tests.

f) Prefer named exports over default exports since they can't be implicitly aliased, which makes it easier to navigate the code.

g) In method overrides, always call the super method (this. super(...arguments) in ES6 classes and components, super.methodName(...arguments) in old-style classes like widgets).

h) Exception: do not call the super function in the component that extends the owl.Component. On owl. Component, setup is assumed to be empty, and the render & constructor cannot be overridden.

i) Prefer the method definition syntax for declaring methods (functions that need this) in plain objects.

j) Avoid writing component templates in javascript scripts using owl.xml; instead, extract the template to a different XML file that is stored alongside the java code.

k) Assignments should always be their own statement; never utilize an assignment expression's value for anything.

CSS coding guidelines

All of your classes should be prefixed with o_<module_name>, where the module name is either the technical name of the module (such as "sell" or "im chat") or the primary route that the module has reserved (mostly for website modules, such as "o forum" for the website forum module). The web client is the lone exception to this rule; it merely uses the o_ prefix.

a) Don't use the id tag

b) Use native Bootstrap classes

c) To name a class, apply underscore lowercase


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message