Foundations of Odoo Development
introduction
Odoo is a full-fledged, open-source ERP software designed to meet diverse business needs with its modular architecture. One of its greatest strengths is its flexibility—when a situation demands a new feature or functionality, you don’t need to modify the existing codebase. Instead, Odoo allows you to extend and customize effortlessly by inheriting code.
Odoo employs a modular, multi-tier architecture, heavily influenced by the Model-View-Controller (MVC) design pattern.
At its core, Odoo’s framework is built around a client-server model, with a PostgreSQL database back end, a Python-based application layer, and a web-based frontend powered by JavaScript and XML.
Modules
Each module encapsulates specific business functionalities (e.g., sales, inventory, accounting). This modularity is key to Odoo's flexibility and scalability.At their core, Odoo modules are simply directories within the Odoo addons path. These directories contain Python files (.py), XML files (.xml), and potentially other static assets.
my_module/
├── __init__.py # Initializes Python modules
├── __manifest__.py # Module metadata and dependencies
├── controllers/ # Web controllers (HTTP routes)
│ └── __init__.py # Initializes controllers module
├── models/ # Business models (ORM classes)
│ ├── __init__.py # Initializes models module
│ └── my_model.py # Custom business logic
├── views/ # XML UI definitions (forms, lists, menus)
│ └── my_model_views.xml # Views for the model
├── security/ # Access control and security rules
│ ├── ir.model.access.csv # Model access rights
│ └── security.xml # Record rules and groups
├── data/ # Demo or initial data (XML/CSV)
│ └── my_model_data.xml # Preloaded records
├── static/ # Web assets (JS, CSS, images)
│ └── description/ # Module icon and description for App list
│ └── icon.png # Icon shown in the Odoo app store
└── tests/ # Unit or functional tests
└── test_my_model.py # Python test cases
The __manifest__.py is Mission Control: This crucial file acts as the module's descriptor.It uniquely identifies your module within the Odoo ecosystem. It contains essential information like the module's name, version, author, dependencies, website, description, and the list of data files to be loaded. It's the first file Odoo reads to understand your module
Syntax and Structure:
The __manifest__.py file is a standard Python dictionary. Here's a breakdown of the common keys and their values:
{
'name': '', # Module name shown in Odoo
'version': '', # Module version
'summary': '', # Short description in apps list
'description': '', # Long description of the module
'author': '', # Author of the module
'website': '', # Website of the author/company
'license': '', # License type (e.g. LGPL-3)
'category': '', # App category in Odoo store
'depends': [], # List of dependent modules
'data': [], # Data files to load (views, security, etc.)
'demo': [], # Demo data for testing or demo purposes
'qweb': [], # QWeb templates for frontend
'installable': True, # Whether module can be installed
'application': False, # Marks it as an application (visible in app list)
'auto_install': False, # Auto-install if dependencies are met
}
ex:
{
'name': 'My Awesome Module',
'version': '1.0',
'summary': 'A short and catchy description of my module.',
'description': """
A more detailed explanation of what this module does.
It can span multiple lines.
""",
'author': 'Your Name or Company Name',
'website': 'https://www.yourwebsite.com',
'license': 'LGPL-3', # Or other appropriate license
'category': 'Specific Industry/Category',
'depends': ['sale', 'mail'],
'data': [
'security/ir.model.access.csv',
'views/my_model_view.xml',
'views/my_other_view.xml',
'data/initial_data.xml',
'wizards/my_wizard_view.xml',
],
'demo': [
'demo/demo_data.xml',
],
'qweb': [
'static/src/xml/my_template.xml',
],
'installable': True,
'application': False,
'auto_install': False,
}
Models
Models in Odoo are classes that represent database tables using the Odoo ORM (Object-Relational Mapping). They define how data is stored, validated, and manipulated.
Defining a model using the models.Model class.To define a new model in Odoo, you create a Python file (e.g., models/my_model.py) and define a class that inherits from models.Model. Odoo automatically handles the underlying database table creation and management based on your model definition.
from odoo import models, fields, api
class MyModel(models.Model):
_name = 'my.model' # Required: A unique technical name for the model
_description = 'Description of My Model' # Optional: A human-readable description
_name: This is a mandatory attribute. It's the unique technical identifier for your model within Odoo's system. It's typically written in lowercase with dots separating logical parts (e.g., library.book, sale.order.line). Odoo uses this name int
_description: This is an optional attribute that provides a human-readable description of your model. It's often used in the Odoo interface and documentation.
Creating fields to store data.
Fields define the attributes of your model and correspond to columns in the database table.
1. Char
- Use: Stores short text (strings).
-
Syntax:
fields.Char(string='Label', required=True, help='Tooltip')
-
Example:
name = fields.Char(string="Name", required=True, help="Product Name")
2. Boolean
- Use: Stores True or False (checkbox).
-
Syntax:
fields.Boolean(string='Label', default=False)
-
Example:
active = fields.Boolean(string="Active", default=True)
3. Date
- Use: Stores calendar dates.
-
Syntax:
fields.Date(string='Label', help='Tooltip')
-
Example:
start_date = fields.Date(string="Start Date")
4. Datetime
- Use: Stores both date and time.
-
Syntax:
fields.Datetime(string='Label')
-
Example:
create_date = fields.Datetime(string="Created On")
5. Float
- Use: Stores decimal numbers.
-
Syntax:
fields.Float(string='Label', digits=(precision, scale))
-
Example:
discount = fields.Float(string="Discount", digits=(10, 2))
6. Integer
- Use: Stores whole numbers.
-
Syntax:
fields.Integer(string='Label')
-
Example:
quantity = fields.Integer(string="Quantity")
7. Html
- Use: Stores HTML content (rich text editor).
-
Syntax:
fields.Html(string='Label')
-
Example:
notes = fields.Html(string="Internal Notes")
8. Binary
- Use: Stores binary files (images, PDFs, etc.).
-
Syntax:
fields.Binary(string='Label')
-
Example:
image = fields.Binary(string="Company Logo")
9. Selection
- Use: Dropdown with predefined options.
-
Syntax:
fields.Selection([('key1', 'Label 1'), ('key2', 'Label 2')], default='key1')
-
Example:
state = fields.Selection([ ('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done') ], default='draft', string="Status")
🔸 Relational Field Types
10. Many2one
- Use: Links current model to a single record in another model (foreign key).
-
Syntax:
fields.Many2one('target.model', string='Label')
-
Example:
partner_id = fields.Many2one('res.partner', string="Customer")
11. One2many
- Use: Links one record to many in another model (inverse of Many2one).
-
Syntax:
fields.One2many('target.model', 'inverse_field', string='Label')
-
Example:
order_line_ids = fields.One2many('sale.order.line', 'order_id', string="Order Lines")
12. Many2many
- Use: Links many records to many (symmetric relationship).
-
Syntax:
fields.Many2many('target.model', string='Label')
-
Example:
tag_ids = fields.Many2many('project.tags', string="Tags")
Odoo Field Parameters
These parameters help control how fields behave in Odoo models, including their display, validation, default values, dynamic computation, and more.
1. string
Description: UI label for the field (defaults to field name if not specified).
Example:
string="Customer Name"
2. required
Description: Makes the field mandatory in the UI.
Possible Values: True / False
Example:
required=True
3. readonly
Description: Makes the field uneditable in the UI.
Possible Values: True / False
Example:
readonly=True
4. default
Description: Sets a default value for new records (can be static or dynamic).
Example:
default=0
default=lambda self: self.env.user.name
5. help
Description: Adds a tooltip to guide users.
Example:
help="Enter the full customer name"
6. store
Description: Stores the value of a computed field in the database (enables search/filtering).
Possible Values: True / False
Example:
store=True
7. compute
Description: References the method used to compute the field's value dynamically.
Example:
compute="_compute_total"
8. invisible
Description: Hides the field from the UI (used in views).
Possible Values: True / False
Example:
invisible=True
9. translate
Description: Allows the field to be translated into multiple languages.
Possible Values: True / False
Example:
translate=True
10. states
Description: Dynamically changes field behavior based on the record's state.
Usage: A dictionary mapping states to a list of (attribute, value) pairs.
Example:
states={ 'done': [('readonly', True)], 'draft': [('invisible', True)] }
Odoo Model Methods
Model methods define the behavior of your data and allow you to perform actions on records. Odoo provides several built-in methods for common data manipulation tasks. You can also define your own custom methods to implement specific business logic.
Using Built-in Methods in Odoo
Odoo allows us to define our own methods and also provides several built-in methods that are commonly used for interacting with models and records. These built-in methods are already defined in Odoo’s base classes and can be inherited and customized as needed.
You can access any model in Odoo using the syntax:
model = self.env['model.name']
For example:
partner = self.env['res.partner'].create({'name': 'John Doe'})
This means you’re calling the built-in create method on the res.partner model.
Common Built-in Methods in Odoo
Method |
Purpose |
Syntax Example | Usage Example |
---|---|---|---|
create | Add new record | def create(self, vals): ... | self.env['model'].create({'field': 'value'}) |
read | Read fields | def read(self, fields=None): ... | self.env['model'].read([ids], [fields]) |
write | Update record(s) | def write(self, vals): ... | record.write({'field': 'new_value'}) |
unlink | Delete record(s) | def unlink(self): ... | record.unlink() |
search | Find by domain | def search(self, domain): ... | self.env['model'].search([('field', '=', value)]) |
browse | Get record by ID | def browse(self, ids): ... | self.env['model'].browse(id) |
Customizing Built-in Methods
These methods can also be overridden to include custom logic. Here’s how you can override the write method:
def write(self, vals):
# custom logic before update
result = super().write(vals)
# custom logic after update
return result
Similarly, you can override other built-in methods like create, unlink, etc.
Example – Customizing create:
def create(self, vals):
# custom pre-processing
record = super().create(vals)
# custom post-processing
return record
Understanding This Code:
partner = self.env['res.partner'].create({'name': 'John Doe'})
Breakdown:
- self.env: Gives access to Odoo’s environment, including models and records.
- ['res.partner']: Refers to the partner model.
- create({...}): Calls the built-in create method to create a new partner record with the provided values.
These built-in methods are already written in Odoo’s core framework. When we use them in our custom models, we’re effectively inheriting their functionality. You don’t need to re-implement them unless you want to add or modify behavior. That’s the power of object-oriented inheritance in Odoo.
Custom Method Definition
In Odoo, your models are defined as Python classes that inherit from Odoo's base model classes (primarily models.Model). Within these classes, you define both your data fields and your custom business logic through methods.
from odoo import models, fields, api
class YourModuleName(models.Model):
_name = 'your.model.technical.name'
_description = 'A human-readable description of your model'
# --- Field Definitions ---
field_one = fields.Char(string='Field One Label')
field_two = fields.Integer(string='Field Two Label', default=0)
# ... more field definitions ...
# --- Custom Method Definitions ---
def your_custom_method(self, parameter1, parameter2=None):
"""
A docstring explaining what this method does.
:param parameter1: Description of the first parameter.
:param parameter2: Description of the second parameter (optional).
:return: What the method returns (if anything).
"""
# Your custom logic goes here
print(f"Executing your_custom_method with parameter1: {parameter1}, parameter2: {parameter2}")
processed_value = parameter1 * 2
if parameter2 is not None:
processed_value += parameter2
return processed_value
def another_method(self):
"""Another custom method."""
for record in self: # Iterate through the recordset
print(f"Processing record with name: {record.field_one}")
# Perform actions on each record in the self recordset
record.field_two += 1
record.write({'field_two': record.field_two})
self: The first parameter of any instance method in an Odoo model (and Python classes in general) is always self. self refers to the recordset on which the method is called. Even if you call the method on a single record, self will be a recordset of size 1. You use self to access the fields of the records in the recordset and to interact with the Odoo environment (self.env).
Model Inheritance in Odoo
Model inheritance is a powerful feature in Odoo that allows you to extend and customize existing models without modifying the core code
Inheriting Existing Models
This is used when you want to add new fields or methods to an existing model.
Syntax: Inheriting a Model
from odoo import models, fields
class InheritModel(models.Model):
_inherit = 'model.name' # Replace with actual model name
new_field = fields.Char(string="New Field Name")
def new_method(self):
# Your custom logic here
pass
Example: Add a New Field to res.partner
from odoo import models, fields
class PartnerInherit(models.Model):
_inherit = 'res.partner'
customer_code = fields.Char(string='Customer Code')
Full Example in a Model
from odoo import models, fields, api
class CustomModel(models.Model):
_name = 'custom.model'
_description = 'Example Custom Model'
name = fields.Char(string="Name", required=True, help="Enter the name")
active = fields.Boolean(string="Active", default=True)
start_date = fields.Date(string="Start Date")
total = fields.Monetary(string="Total", currency_field='currency_id')
currency_id = fields.Many2one('res.currency', string="Currency")
partner_id = fields.Many2one('res.partner', string="Customer")
notes = fields.Html(string="Notes")
status = fields.Selection([
('draft', 'Draft'),
('approved', 'Approved'),
('done', 'Done')
], default='draft', string="Status")
Odoo Model Types
Odoo provides multiple base classes for defining models. The most common ones are:
1. models.Model – Persistent Model
Definition: This is the standard model class in Odoo. Data stored in this model is persistent — it is saved in the database and stays there unless deleted.
Used for: Main business models (e.g., sale.order, res.partner, product.template, etc.).
Syntax:
from odoo import models, fields class MyModel(models.Model): _name = 'my.model' name = fields.Char(string='Name')
Features:
- Data is stored permanently.
- Can be used in menus, views, and actions.
- Supports security access rules.
Example:
class LibraryBook(models.Model): _name = 'library.book' name = fields.Char(string="Title") author = fields.Many2one('res.partner', string='Author')
2. models.TransientModel – Temporary Model / Wizard Model
Definition: Used for temporary data, like wizards or popups. Data is automatically deleted after some time (usually after the session ends or a few minutes).
Used for: Wizards, short-lived forms, and temporary user interactions.
Syntax:
from odoo import models, fields class MyWizard(models.TransientModel): _name = 'my.wizard' name = fields.Char(string='Temporary Name')
Features:
- Data is not persisted long-term.
- Ideal for wizards, assistants, or temporary UI elements.
- No security rules applied unless specified.
- Can't be accessed directly like res.partner from the menu.
Example:
class AttendanceWizard(models.TransientModel): _name = 'attendance.wizard' date_from = fields.Date(string="Start Date") date_to = fields.Date(string="End Date") def generate_report(self): # temporary logic return { 'type': 'ir.actions.act_window', 'name': 'Report', # more return values... }
3. models.AbstractModel – Reusable Logic Only (No DB Table)
Definition: Used for shared logic, with no table created in the database.
Used for: Utility functions, common business logic, and mixins.
Syntax:
class MyMixin(models.AbstractModel): _name = 'my.mixin'
Features:
- No actual records are stored.
- Inherited by other models for common methods or functionality.
- Can be used as Mixin classes (e.g., mail.thread).
Example:
class TimestampMixin(models.AbstractModel): _name = 'timestamp.mixin' def get_timestamp(self): return fields.Datetime.now()
Odoo Views
Views in Odoo are XML files that define how records of your models are displayed and interacted with in the user interface. They control everything from the layout of forms to the columns in lists and the organization of kanban boards. Understanding views is crucial for crafting a user-friendly and efficient Odoo application.
They support embedding HTML and referencing CSS to create rich, styled, and user-friendly interfaces. The XML structure acts as a wrapper or container for UI components, while HTML tags (like <div>, <span>, <table>) and CSS classes (built-in or custom) provide styling and layout control.
XML (eXtensible Markup Language) is a markup language used to define structured data. It’s similar to HTML but is designed to store and transport data, not display it. In Odoo, XML is used to define views, menus, actions, access rights, and more.
Most Common Odoo XML Tags & Their Purpose
Here is a list of commonly used Odoo XML tags, along with their purpose:
1. <odoo>
- Purpose: Root tag in all XML files loaded by Odoo.
2. <data>
- Purpose: Wraps all record definitions and view elements.
3. <record>
- Purpose: Defines a database record (views, actions, menus, etc.).
4. <field>
- Purpose: Defines the value of a field in a record or inside form/tree views.
5. <form>, <tree>, <kanban>, <calendar>, <graph>
- Purpose: UI views that define the type of view structure.
6. <sheet>
- Purpose: Used inside <form> to wrap fields in a clean layout.
7. <group>
- Purpose: Groups fields (horizontal or vertical) inside <sheet> or <form>.
8. <notebook>
- Purpose: Creates tabs inside forms.
9. <page>
- Purpose: Tab inside <notebook>.
10. <button>
- Purpose: Adds a button (action, workflow, etc.) inside views.
11. <menuitem>
- Purpose: Creates menu entries (top and submenus).
12. <act_window>
- Purpose: Defines an action to open a view.
13. <search>
- Purpose: Defines a search view.
14. <filter>
- Purpose: Adds filters to a search view.
15. <xpath>
- Purpose: Used for view inheritance/modification.
Basic Structure of an Odoo XML File
<odoo>
<data>
<!-- All your records (views, actions, menus, etc.) go here -->
</data>
</odoo>
Full XML View Definition Example
<odoo> <data> <!-- Tree View --> <record id="model_name_tree" model="ir.ui.view"> <field name="name">model.name.tree</field> <field name="model">model.name</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="date"/> </tree> </field> </record> <!-- Form View --> <record id="model_name_form" model="ir.ui.view"> <field name="name">model.name.form</field> <field name="model">model.name</field> <field name="arch" type="xml"> <form> <sheet> <group> <field name="name"/> <field name="description"/> </group> </sheet> </form> </field> </record> <!-- Action --> <record id="action_model_name" model="ir.actions.act_window"> <field name="name">Model Name</field> <field name="res_model">model.name</field> <field name="view_mode">tree,form</field> </record> <!-- Menu --> <menuitem id="menu_model_root" name="Root Menu"/> <menuitem id="menu_model_name" name="Model Name" parent="menu_model_root" action="action_model_name"/> </data> </odoo>
Explanation:
-
Tree View:
- This creates a tree view (model_name_tree) for the model.name model, displaying fields name and date.
-
Form View:
- Defines a form view (model_name_form) for the same model, with fields name and description inside a group layout within a sheet.
-
Action:
- Creates an action (action_model_name) to open the model’s views in tree and form mode when triggered.
-
Menu:
- A root menu item (menu_model_root) and a sub-menu item (menu_model_name) are created, which links to the action defined above.
5. Record:
- A <record> tag defines a database record in Odoo, representing elements like views, actions, or menus. Each <record> has a unique id used to reference it in the system. For example, a <record> for an action defines how views (tree, form, etc.) are opened when triggered. It’s essential for creating and modifying key components in Odoo.
6. sheet:
- The <sheet> tag visually wraps the form content inside a clean, card-style box. It adds padding, proper alignment, and standard UI styling for better presentation. The <sheet> tag is always used inside a <form> to structure and organize the fields effectively.
Odoo Widgets
Here's a list of Odoo widgets, along with example code snippets and how you might use them in a GitHub Markdown file.
1. widget="handle"
- Use: Displays a handle for reordering records in a list view.
- Code Example:
<field name="sequence" widget="handle"/>
- Markdown:
Use `widget="handle"` to add a drag-and-drop handle for reordering items in a list view. Example: ```xml <field name="sequence" widget="handle"/>
2. widget="boolean_toggle"
- Use: Displays a toggle switch for boolean fields.
- Code Example:
<field name="active" widget="boolean_toggle"/>
- Markdown:
`widget="boolean_toggle"` provides a user-friendly toggle switch for boolean values. ```xml <field name="active" widget="boolean_toggle"/>
3. widget="domain"
- Use: Displays a domain editor for filtering records.
- Code Example:
<field name="filter_domain" widget="domain"/>
- Markdown:
The `widget="domain"` allows users to visually construct complex search filters. ```xml <field name="filter_domain" widget="domain"/>
4. widget="code"
- Use: Displays a code editor for text fields.
- Code Example:
<field name="python_code" widget="code" options="{'mode': 'python'}"/>
- Markdown:
Use `widget="code"` to provide a syntax-highlighted code editor. You can specify the language mode. ```xml <field name="python_code" widget="code" options="{'mode': 'python'}"/>
5. widget="many2many_tags"
- Use: Displays many2many relationships as tags.
- Code Example:
<field name="tag_ids" widget="many2many_tags"/>
- Markdown:
`widget="many2many_tags"` shows related records as interactive tags. ```xml <field name="tag_ids" widget="many2many_tags"/>
6. widget="statusbar"
- Use: Displays the status of a record in a visual status bar.
Code Example:
<field name="state" widget="statusbar" statusbar_visible="draft,sent,done"/>
- Markdown:
The `widget="statusbar"` gives a clear visual representation of a record's workflow. ```xml <field name="state" widget="statusbar" statusbar_visible="draft,sent,done"/>
7. widget="radio"
- Use: Displays a radio button group for selection fields.
- Code Example:
<field name="selection_field" widget="radio"/>
- Markdown:
`widget="radio"` provides a radio button interface for selection fields. ```xml <field name="selection_field" widget="radio"/>
8. widget="char"
- Use: Displays a simple text input field (default behavior, often implicit).
- Code Example:
<field name="name" widget="char"/>
- Markdown:
`widget="char"` is the standard text input widget. ```xml <field name="name" widget="char"/>
9. widget="image"
- Use: Displays an image field.
- Code Example:
<field name="image" widget="image"/>
- Markdown:
`widget="image"` renders image fields within the view. ```xml <field name="image" widget="image"/>
10. widget="url"
- Use: Displays a URL as a clickable link.
- Code Example:
<field name="website" widget="url"/>
- Markdown:
`widget="url"` transforms text into clickable web links. ```xml <field name="website" widget="url"/>
11. widget="phone"
- Use: Displays a phone number as a clickable link (for calling).
- Code Example:
<field name="phone" widget="phone"/>
- Markdown:
`widget="phone"` renders phone numbers as clickable links for calling. ```xml <field name="phone" widget="phone"/>
12. widget="reference"
- Use: Displays a reference field (linking to various models).
- Code Example:
<field name="related_record" widget="reference"/>
- Markdown:
`widget="reference"` shows a field that links to records from different models. ```xml <field name="related_record" widget="reference"/>
13. widget="timezone_mismatch"
- Use: Displays a warning if the user's timezone differs from the record's timezone.
- Code Example:
<field name="date_time" widget="timezone_mismatch"/>
- Markdown:
`widget="timezone_mismatch"` warns users about potential timezone discrepancies. ```xml <field name="date_time" widget="timezone_mismatch"/>
14. widget="statinfo"
- Use: Displays statistical information in a compact format.
- Code Example:
<field name="total_sales" widget="statinfo"/>
- Markdown:
`widget="statinfo"` presents key metrics in a visually concise way. ```xml <field name="total_sales" widget="statinfo"/>
15. widget="image_url"
- Use: Displays an image from a URL.
- Code Example:
<field name="image_url" widget="image_url"/>
- Markdown:
`widget="image_url"` renders images fetched from external URLs. ```xml <field name="image_url" widget="image_url"/>
- 16. widget="many2one_avatar_user `
- Use: Displays a user avatar based on a many2one field of res.users.
- Module: sale_management
- Field Type: Many2one
- Code Example:
<field name="user_id" widget="many2one_avatar_user"/>
- Markdown:
`widget="many2one_avatar_user"` displays a user's avatar. ```xml <field name="user_id" widget="many2one_avatar_user"/>
17. widget="badge"
- Use: Displays a badge based on the selection field of state.
- Module: sale_management
- Field Type: Selection
- Code Example:
<field name="state" widget="badge" options="{'colors': {'draft': 'secondary', 'confirmed': 'primary'}}"/>
- Markdown:
`widget="badge"` displays a colored badge based on a selection field. ```xml <field name="state" widget="badge" options="{'colors': {'draft': 'secondary', 'confirmed': 'primary'}}"/>
18. widget="char_emojis" / widget="text_emojis"
- Use: Allows entering and displaying emojis in char or text fields.
- Module: mass_mailing
- Field Type: Char or Text
- Code Example:
<field name="description" widget="text_emojis"/>
- Markdown:
`widget="char_emojis"` / `widget="text_emojis"` enables emoji input and display. ```xml <field name="description" widget="text_emojis"/>
19. widget="remaining_days"
- Use: Calculates and displays the remaining days based on a datetime field.
- Module: purchase
- Field Type: Datetime
- Code Example:
<field name="date_deadline" widget="remaining_days"/>
- Markdown:
`widget="remaining_days"` shows the remaining days until a deadline. ```xml <field name="date_deadline" widget="remaining_days"/>
20. widget="Countdown" (Website Widget)
- Use: A website-specific widget (drag and drop) to create countdown timers.
- Module: website
- Markdown:
`widget="Countdown"` (Website Widget) creates countdown timers on website pages.
21. widget="color_picker"
- Use: Allows selecting a color via a color picker for an integer field.
- Module: sale_management
- Field Type: Integer
- Code Example:
<field name="color" widget="color_picker"/>
- Markdown:
`widget="color_picker"` provides a color selection tool. ```xml <field name="color" widget="color_picker"/>
22. widget="kanban_activity"
- Use: Displays activities in a Kanban view, related to a mail.activity object via a one2many field.
- Module: sale_management
- Field Type: one2many
- Code Example:
<field name="activity_ids" widget="kanban_activity" optional="show"/>
- Markdown:
`widget="kanban_activity"` displays activities in a Kanban card. ```xml <field name="activity_ids" widget="kanban_activity" optional="show"/>
23. widget="many2one_avatar_employee"
- Use: Displays an employee avatar based on a many2one field of hr.employee.
- Module: hr_expense
- Field Type: Many2one
- Code Example:
<field name="employee_id" widget="many2one_avatar_employee"/>
- Markdown:
`widget="many2one_avatar_employee"` displays an employee's avatar. ```xml <field name="employee_id" widget="many2one_avatar_employee"/>
24. widget="product_discount"
- Use: A specialized widget for displaying and handling product discounts (float field).
- Module: sale_management
- Field Type: Float
- Code Example:
<field name="discount" widget="product_discount"/>
- Markdown:
`widget="product_discount"` handles product discount display. ```xml <field name="discount" widget="product_discount"/>
25. widget="account_resequence_widget"
- Use: Special widget to re-sequence accounting entries.
- Module: account
- Field Type: Text
- Markdown:
`widget="account_resequence_widget"` re-sequences accounting entries.
26. widget="grouped_view_widget"
- Use: Used for grouped views in accounting, especially for invoice grouping.
- Module: account
- Field Type: Text
- Markdown:
`widget="grouped_view_widget"` is used for grouped accounting views.
27. widget="task_with_hours"
- Use: Connect to a task in a task management system.
- Module: hr_timesheet
- Field Type: many2one
- Code Example:
<field name="task_id" widget="task_with_hours"/>
- Markdown:
`widget="task_with_hours"` connects to a task with hours. ```xml <field name="task_id" widget="task_with_hours"/>
28. widget="timesheet_uom_no_toggle"
- Use: Time sheet user management control.
- Module: project
- Field Type: Float
- Markdown:
`widget="timesheet_uom_no_toggle"` controls timesheet user management.
29. widget="forecast_widget"
- Use: Forecast values.
- Module: stock
- Field Type: Float
- Markdown:
`widget="forecast_widget"` displays forecast values.
30. widget="stock_rescheduling_popover"
- Use: Stock rescheduling information.
- Module: mrp
- Field Type: Char
- Markdown:
`widget="stock_rescheduling_popover"` displays stock rescheduling info.
Frequently Used Class Styles in Odoo
Here’s a more structured and ordered version of the breakdown for Odoo's commonly used CSS classes:
1. oe_read_only
Use: Makes a field or element read-only, preventing user interaction.
Example:
<field name="field_name" class="oe_read_only"/> <div class="oe_read_only">This text is read-only.</div>
Markdown:
The oe_read_only class prevents user interaction with a field or element.
2. oe_edit_only
Use: Makes a field or element visible only in edit mode.
Example:
<field name="field_name" class="oe_edit_only"/> <div class="oe_edit_only">This appears only during editing.</div>
Markdown:
The oe_edit_only class displays an element only when the form is in edit mode.
3. oe_highlight
Use: Visually highlights an element, often used for important information or warnings.
Example:
<field name="field_name" class="oe_highlight"/> <div class="oe_highlight">This is highlighted.</div>
Markdown:
The oe_highlight class provides a visual highlight to draw attention to an element.
4. oe_inline
Use: Displays an element inline, similar to display: inline;.
Example:
<span class="oe_inline">Inline text</span> <span class="oe_inline">Another inline text</span>
Markdown:
The oe_inline class renders elements horizontally, side by side.
5. oe_right and oe_left
Use: Aligns elements to the right or left, respectively.
Example:
<div class="oe_right">Right-aligned text</div> <div class="oe_left">Left-aligned text</div>
Markdown:
The oe_right and oe_left classes control text or element alignment.
6. oe_bold
Use: Applies bold text styling.
Example:
<span class="oe_bold">Bold text</span>
Markdown:
The oe_bold class renders text in bold.
7. oe_italic
Use: Applies italic text styling.
Example:
<span class="oe_italic">Italic text</span>
Markdown:
The oe_italic class renders text in italics.
8. o_form_sheet, o_form_sheet_bg, o_form_label, o_form_input
Use: These classes are part of Odoo's form layout structure, providing consistent styling for forms.
Example:
<div class="o_form_sheet"> <div class="o_form_sheet_bg"> <label for="name" class="o_form_label">Name</label> <input type="text" name="name" class="o_form_input"/> </div> </div>
Markdown:
Odoo's form styling relies on classes like o_form_sheet, o_form_sheet_bg, o_form_label, and o_form_input for consistent layouts.
9. o_list_view and o_list_table
Use: These classes are used to style list views and their tables.
Example:
<tree class="o_list_view"> <field name="name"/> </tree>
Markdown:
o_list_view and o_list_table are core classes for styling list views.
10. o_kanban_view and o_kanban_record
Use: These classes are used to style kanban views and individual kanban cards.
Example:
<kanban class="o_kanban_view"> <templates> <t t-name="kanban-box"> <div class="o_kanban_record"> <field name="name"/> </div> </t> </templates> </kanban>
Markdown:
o_kanban_view and o_kanban_record are fundamental classes for kanban view styling.
This list provides a well-organized overview of commonly used CSS classes in Odoo, their purposes, and examples of how they’re applied in the code.
Frequently Used T-directives in Odoo XML
Odoo's XML templating engine relies heavily on T-directives (prefixed with t-) for dynamic content generation. Here's a breakdown of commonly used T-directives:
1. t-as
Use: Assigns an alias to the current item within a t-foreach loop.
Example:
<t t-foreach="records" t-as="record"> <t t-esc="record.name"/> </t>
Markdown:
t-as creates an alias for the current item in a loop, simplifying access to its properties.
2. t-call
Use: Includes another template within the current template.
Example:
<t t-call="module.template_name"/>
Markdown:
t-call allows you to reuse template snippets, promoting code modularity.
3. t-else
Use: Defines the alternative block to execute when the condition in t-if is false.
Example:
<t t-if="condition"> ... </t> <t t-else=""> ... </t>
Markdown:
t-else provides an alternative execution path for t-if conditions.
4. t-esc
Use: Evaluates an expression and escapes the result for safe HTML output.
Example:
<t t-esc="record.name"/>
Markdown:
t-esc safely displays dynamic content, preventing potential cross-site scripting (XSS) vulnerabilities.
5. t-foreach
Use: Iterates over a collection (e.g., a list or recordset).
Example:
<t t-foreach="records" t-as="record"> ... </t>
Markdown:
t-foreach loops through a set of items, enabling dynamic content generation for each item.
6. t-if
Use: Conditionally renders a block of content based on a boolean expression.
Example:
<t t-if="record.active"> ... </t>
Markdown:
t-if allows you to display or hide content based on specific conditions.
7. t-key
Use: Specifies a unique key for an element, particularly useful within t-foreach loops for efficient DOM updates.
Example:
<t t-foreach="records" t-as="record"> <div t-key="record.id"> ... </div> </t>
Markdown:
t-key optimizes DOM updates in loops by providing unique identifiers for elements.
8. t-name
Use: Assigns a name to a template, making it reusable via t-call.
Example:
<t t-name="module.template_name"> ... </t>
Markdown:
t-name defines a named template that can be invoked using t-call.
9. t-out
Use: Evaluates an expression and outputs the result without HTML escaping (use with caution).
Example:
<t t-out="record.html_field"/>
Markdown:
t-out outputs raw HTML, so use it carefully to avoid XSS vulnerabilities.
10. t-set
Use: Assigns a value to a variable within the template scope.
Example:
<t t-set="total" t-value="record.price * record.quantity"/>
Markdown:
t-set creates or modifies variables within the template's context.
11. t-value
Use: Specifies the value to be assigned to a variable using t-set.
Example:
<t t-set="total" t-value="record.price * record.quantity"/>
Markdown:
t-value defines the value that t-set assigns to a variable.
This format preserves your content exactly as requested, while presenting it in a more structured, easy-to-read layout.
View Inheritance and Overriding in Odoo
In Odoo, views control how data from models is displayed to users. These views can be form views, tree views, kanban views, calendar views, etc. When customizing Odoo without breaking the core system or future upgrades, view inheritance is the preferred approach. However, for significant changes, overriding views is an option.
1. View Inheritance in Odoo
Purpose:
View inheritance allows you to modify or extend existing views (such as form, tree, kanban) without directly changing the original source code. This ensures that your customizations remain upgrade-safe and compatible with Odoo’s modular architecture.
Steps to Inherit a View:
-
Create a New XML File
Place it inside your module’s views/ directory (e.g., views/sale_order_inherit.xml). -
Add to Manifest
Reference the XML file in your module’s manifest.py:'data': [ 'views/sale_order_inherit.xml', ],
-
Define the Inherited View
In the XML file, define the inherited view using the <record> tag. Reference the original view using inherit_id and modify it using XPath.
Example:<odoo> <record id="sale_order_form_inherit" model="ir.ui.view"> <field name="name">sale.order.form.inherit</field> <field name="model">sale.order</field> <field name="inherit_id" ref="sale.view_order_form"/> <field name="arch" type="xml"> <!-- Add a field after 'partner_id' --> <xpath expr="//field[@name='partner_id']" position="after"> <field name="x_custom_field"/> </xpath> </field> </record> </odoo>
Key Attributes:
Field | Description |
---|---|
inherit_id | The XML ID of the original view you're modifying (use ref="" syntax). |
arch | Contains the XML definition that uses XPath to target specific areas for modification. |
xpath | Locates the exact node where your content will be inserted. |
position | Controls where to place your element (e.g., after, before, replace, etc.). |
Common XPath + Position Examples:
XPath Expression | What it Targets |
---|---|
//field[@name='partner_id'] | Field by its name attribute |
//group[@string='Delivery'] | Group by its label |
//notebook/page[@string='Other'] | Specific page inside a notebook |
//button[@name='action_confirm'] | Button by its name attribute |
Position | Description |
---|---|
after | Adds new content after the matched element |
before | Adds new content before the matched element |
replace | Replaces the matched element completely |
inside | Adds content inside the matched element (as a child) |
attributes | Modifies the attributes (e.g., invisible="1") of the matched tag |
Use Cases of View Inheritance:
- Adding a custom field to a form.
- Changing labels or visibility of fields.
- Reorganizing layouts (e.g., moving fields between groups).
- Adding buttons, tabs, status bars, or widgets.
2. View Overriding in Odoo
Purpose:
When major changes are required and you no longer want to use the default structure, overriding a view allows you to replace the entire view with your own custom version.
Steps to Override a View:
-
Create a New View
Define a new view with the same model and type as the original view. Use a higher priority to ensure your view overrides the original one. -
Set Higher Priority
Set the priority of your custom view to a higher value (default is 16). This ensures your view takes precedence over the original view. -
Define the New Structure
Rewrite the view’s XML structure entirely based on your custom requirements.
Example:<odoo> <record id="sale_order_form_override" model="ir.ui.view"> <field name="name">sale.order.form.override</field> <field name="model">sale.order</field> <field name="priority" eval="20"/> <!-- Ensure it's higher than original --> <field name="arch" type="xml"> <form> <sheet> <group> <field name="name"/> <field name="partner_id"/> <field name="x_custom_field"/> </group> </sheet> </form> </field> </record> </odoo>
Key Considerations When Overriding a View:
Risk | Explanation |
---|---|
Upgrade Fragility | Overridden views will not receive updates if the base module changes. |
Hard to Maintain | You are responsible for maintaining the entire layout and structure. |
Not Modular | Overriding views is discouraged in modular design and should only be done when absolutely required. |
Tools for View Inheritance
XPath Expressions:
- //field[@name='field_name']: Targets a field by its name attribute.
- //group[@name='group_name']: Targets a group by its name attribute.
- //button[@name='action_name']: Targets a button by its name attribute.
Position Attributes:
Attribute | Description |
---|---|
after | Insert the new element after the matched element. |
before | Insert the new element before the matched element. |
replace | Replace the matched element with new content. |
inside | Append the new element inside the matched element. |
attributes | Modify the attributes (e.g., invisible="1") of the matched element. |
Foundations of Odoo Development