Development Book V17: Constraints

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.

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