## 模型约束 Odoo提供了两种方式设置自动验证的不变量: [`Python constraints`](https://www.odoo.com/documentation/9.0/reference/orm.html#openerp.api.constrains "openerp.api.constrains") 和 [`SQL constraints`](https://www.odoo.com/documentation/9.0/reference/orm.html#openerp.models.Model._sql_constraints "openerp.models.Model._sql_constraints"). Python的约束被定义为一种装饰 [`constrains()`](https://www.odoo.com/documentation/9.0/reference/orm.html#openerp.api.constrains "openerp.api.constrains"), 在一个记录集调用。装饰指定领域的参与约束,这样的约束是自动评估时,其中一个是修饰。如果它的不变是不满意的,该方法将提高一个例外: ~~~ python from openerp.exceptions import ValidationError @api.constrains('age') def _check_something(self): for record in self: if record.age > 20: raise ValidationError("Your record is too old: %s" % record.age) # all records passed the test, don't return anything ~~~ 练习 添加Python约束 增加一个约束,检查,教练是不出席在他/她自己的会议出席。 *openacademy/models.py* ~~~ python # -*- coding: utf-8 -*- from openerp import models, fields, api, exceptions class Course(models.Model): _name = 'openacademy.course' ~~~ ~~~ python 'message': "Increase seats or remove excess attendees", }, } @api.constrains('instructor_id', 'attendee_ids') def _check_instructor_not_in_attendees(self): for r in self: if r.instructor_id and r.instructor_id in r.attendee_ids: raise exceptions.ValidationError("A session's instructor can't be an attendee") ~~~ SQL约束是通过模型的属性定义 [`_sql_constraints`](https://www.odoo.com/documentation/9.0/reference/orm.html#openerp.models.Model._sql_constraints "openerp.models.Model._sql_constraints"). 后者是分配到一个列表中的字符串`三元组(名称、sql_definition,消息)`,当`名字`是一个有效的SQL约束的名字,` sql_definition `是 [table_constraint](http://www.postgresql.org/docs/9.3/static/ddl-constraints.html)表达式,和“消息”是错误消息。 练习 添加SQL约束 在 [PostgreSQL的文档](http://www.postgresql.org/docs/9.3/static/ddl-constraints.html) 帮助下 , 添加以下约束: 1. 检查课程简介和课程名称是不同的 2. 使课程名称唯一 *openacademy/models.py* ~~~ python session_ids = fields.One2many( 'openacademy.session', 'course_id', string="Sessions") _sql_constraints = [ ('name_description_check', 'CHECK(name != description)', "The title of the course should not be the description"), ('name_unique', 'UNIQUE(name)', "The course title must be unique"), ] class Session(models.Model): _name = 'openacademy.session' ~~~ 练习 练习6 -添加一个重复的选项 因为我们在课程名称的唯一性上增加了一个约束,不可能使用“复制”功能(重复)。 重新实现您自己的“复制”方法,它允许复制的课程对象,将原来的名字改为“复制[原名称]”。 *openacademy/models.py* ~~~ python session_ids = fields.One2many( 'openacademy.session', 'course_id', string="Sessions") @api.multi def copy(self, default=None): default = dict(default or {}) copied_count = self.search_count( [('name', '=like', u"Copy of {}%".format(self.name))]) if not copied_count: new_name = u"Copy of {}".format(self.name) else: new_name = u"Copy of {} ({})".format(self.name, copied_count) default['name'] = new_name return super(Course, self).copy(default) _sql_constraints = [ ('name_description_check', 'CHECK(name != description)', ~~~