To enforce data integrity, models also support two types of constraints: SQL and Python

SQL constraints are added to the database table definition and are enforced directly by PostgreSQL. They are defined using the _sql_constraints class attribute. It is a list of tuples with: the constraint identifier name; the SQL for the constraint; and the error message to use.

A common use case is to add unique constraints to models. Suppose we don't want to allow two active tasks with the same title:

# class TodoTask(models.Model): 
    _sql_constraints = [ 
        ('todo_task_name_uniq', 
         'UNIQUE (name, active)', 
         'Task title must be unique!')] 

Python constraints can use a piece of arbitrary code to check the conditions. The checking function should be decorated with @api.constraints, indicating the list of fields involved in the check. The validation is triggered when any of them is modified and will raise an exception if the condition fails.

For example, to validate that a Task name is at least five characters long, we could add the following constraint:

from odoo.exceptions import ValidationError
# class TodoTask(models.Model):
    @api.constrains('name')
    def _check_name_size(self):
        for todo in self:
            if len(todo.name) < 5:
                raise ValidationError('Must have 5 
                chars!')