Relational fields in the Odoo framework are utilized to create
relationships between different models. You can link and get
data from related records by using the associations these
variables establish between records of various models. In real
business scenarios, establishing relational links between
different models is essential for developing and implementing
business functionalities according to specific requirements.
Odoo offers three types of relational fields to facilitate
these connections and data associations. They are
SQL Constraints:
In the model, SQL Constraints are specified through the class
attribute _sql_constraints. This attribute is associated with
the PostgreSQL aspect of the system.
Syntax for writing SQL Constraints:
_sql_constraints = [(name, sql_def, message)]
name: SQL Constraints name.
sql_def: PostgreSQL syntax of the constraint.
message: error message.
For example:
sql_constraints = [('date_order_conditional_required', "CHECK( (state IN ('sale', 'done') AND date_order IS NOT NULL) OR state NOT IN ('sale', 'done') )", "A confirmed sales order requires a confirmation date.")]
The SQL constraint, extracted from the sale order, is designed
to validate that a confirmed sale order includes a
confirmation date. In case of failure, an error message is
displayed. This Python-based SQL constraint is declared before
the coding section, typically within the field declaration
section.
Python Constraints:
Python constraints complement SQL constraints by providing a
flexible way to ensure data consistency, especially in more
complex scenarios. A Python constraint is a method marked with
the @constrains() decorator, tied to a record set. Decorators
specify the fields involved, and these constraints are
automatically checked when the designated fields undergo
modifications. It's a versatile approach to maintain data
consistency in diverse situations.
For example:
@api.constrains('product_id')
def check_product_is_not_kit(self):
"""validation to check whether product is not a kit"""
if self.env['mrp.bom'].search(
['|', ('product_id', 'in', self.product_id.ids), '&',
('product_id', '=', False),
('product_tmpl_id', 'in', self.product_id.product_tmpl_id.ids),
('type', '=', 'phantom')], count=True):
raise ValidationError(
_("A product with a kit-type bill of materials can not have a "
"reordering rule."))
The warehouse's SQL constraint ensures that a product with a
kit-type bill of materials cannot have any reordering rules.
If this condition is violated, an error message is shown.
Computed Fields
In practical business scenarios, there are fields whose values
are calculated based on other fields, either within the same
records or in related records. To handle such situations,
computed fields in Odoo prove to be valuable. These fields
derive their values through a specified function.
The definition of computed fields in Odoo mirrors that of
regular fields, with the exception of the "compute" attribute,
which designates the function responsible for the calculation.
Computed fields dynamically calculate values at runtime and
are not inherently writable or searchable unless explicitly
configured.
Similar to regular fields, a computed field is declared, and
it includes the "compute" attribute specifying the function's
name as a string or the function itself. This mechanism
enables users to efficiently calculate and manage values in
Odoo based on predefined functions.
Dependencies:
Computed fields in Odoo typically rely on the values of other
fields. To establish these dependencies, the compute method
incorporates the depends() decorator. This computation
function dynamically calculates values during runtime. To
ensure efficiency and avoid unnecessary recalculations, it is
crucial to identify which fields depend on the computed field.
This information is added to the depends() decorator,
specifying the fields that influence the computed field's
value.
For example
amount = fields.Float('Amount')
total = fields.Float('Total', compute="_compute_total")
@api.depends('amount')
def _compute_total(self):
for rec in self:
rec.total = 2.0 * rec.amount
Here we can calculate the total by using the function
_compute_total. Computed fields are not stored in the database
by default. So we can use the store attribute to store the
record in the database. We can use the store = True flag.
For example
total = fields.Float('Total', compute="_compute_total", store=True)
Stored values of computed fields can be retrieved similarly to
regular fields, without the need for runtime recalculation.
The ORM (Object-Relational Mapping) system determines when
these stored values should be recomputed and updated, ensuring
efficient and timely handling of the computed field values.
Inverse Function
We know that by default the computed fields are read-only.
Which means the user couldn’t set the value for the computed
fields. In some cases, it might be useful to still be able to
set a value directly. For that Odoo provides the ability to
use an inverse function:
total = fields.Float(compute="_compute_total", inverse="_inverse_total")
amount = fields.Float("Amount")
@api.depends("amount")
def _compute_total(self):
for record in self:
record.total = 2.0 * record.amount
def _inverse_total(self):
for record in self:
record.amount = record.total / 2.0
The compute method is responsible for defining the field's
value, whereas the inverse method establishes the dependencies
of the field. The inverse method is invoked during the
record-saving process, while the compute method is triggered
whenever there is a change in its dependencies.
Related Fields
In practical business scenarios, there are instances where we
need to display the value of a field from a related model in
the current model. In such cases, related fields come into
play, and to implement this, we use the "related" attribute.
For example, consider a Many2one field named partner_id with a
corresponding model (comodel) of res.partners. In this
scenario, a related field named partner_name can be specified
to display relevant information.
partner_id = fields.Many2one("Partner", 'res.partners')
partner_name = fields.Char('Name', related="partner_id.name")
By default, the related fields are not stored in the database,
and also it is read-only and not copied fields. If we want to
store the field record in the database, we can use store =
True attributes.
partner_name = fields.Char('Name', related="partner_id.name", store=True)
Reference Fields
In Odoo, reference fields provide a flexible way to create
dynamic relationships between models. Unlike standard
relational models, where related comodels are predefined,
reference fields allow users to choose both the related model
and a specific record from a selection list. Users first
select the target model and then pick the desired record
within that model. For example, a field named "reference"
could be used to select Purchase Orders, Sale Orders, or
Manufacturing Orders, providing a versatile approach to
establishing connections between different models.
reference_field = fields.Reference(selection='', string='field
name')
For this, we use the selection parameter. We must assign the
model and its name to this parameter.
For example:
reference = fields.Reference(selection="[('sale.order', 'Sale Order'), ('purchase.order', ' Purchase Order')]", string="Reference")
Here, we can choose a Sale Order or Purchase Order, from the same field. Also possible to selection from a function.
reference = fields.Reference(selection="get_model")
@api.model
def get_model(self):
models = self.env['ir.model'].search([])
return [(model.model, model.name), for model in models]
Returned values must be a list.