建立一个Odoo Module (四)- Computed fields、Model constriants
来源:互联网 发布:unity3d speedtree 编辑:程序博客网 时间:2024/05/22 11:27
Computed fields and default values
目前为止,fields 都是直接将数据写到数据库或者从数据库读取数据。Fields 中同样可以通过调用方法获取动态计算值,而不是从数据库中取数据。
创建 computed field 的方式就是给一个 field 设置 compute
属性,并将其值 = method name。那么这个method 就会对 self 这个model的所有record自动生效。
注意
self 不仅可以像普通 python class 定义中一样,调用方法,在 Odoo 中 self 同时代表 recordset,是一个有序的自己 model record 的集合,支持普通python 方法, len(self) and iter(self), 同时也可以将两个recordset 相加。 recs1 + recs2
迭代 self, 将会把 records 中的record 一个一个返回,同时返回的每一个 record 也是一个 recordset,只不过 size == 1。也可以通过 点 来调用 单个record的属性。 record.name
import randomfrom openerp import models, fields, apiclass ComputedModel(models.Model): _name = 'test.computed' name = fields.Char(compute='_compute_name') @api.multi def _compute_name(self): for record in self: record.name = str(random.randint(1, 1e6))
Dependencies
computed field 的值通常是根据其他 field 的 value 计算出来的。Odoo 的 ORM 就希望开发者能够明确指出需要依赖的 field 是哪些,所以提供了一个装饰器 depend()。无论在哪,只要当被依赖的 fields 变动时,Odoo就会根据这个自动重新计算 computed field 的值。
from openerp import models, fields, apiclass ComputedModel(models.Model): _name = 'test.computed' name = fields.Char(compute='_compute_name') value = fields.Integer() @api.depends('value') def _compute_name(self): for record in self: record.name = "Record with value %s" % record.value
练习 4-1
- 在 session model 中添加座位使用的 百分比
- 将这个新增 field 添加到 tree form view 中
- 将这个field 以 process bar的形式展现出来
openacademy/models.py
course_id = fields.Many2one('openacademy.course', ondelete='cascade', string="Course", required=True) attendee_ids = fields.Many2many('res.partner', string="Attendees") # 新增 taken_seats = fields.Float(string="Taken seats", compute='_taken_seats') @api.depends('seats', 'attendee_ids') def _taken_seats(self): for r in self: if not r.seats: r.taken_seats = 0.0 else: r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats # 结束
openacademy/views/openacademy.xml
<field name="start_date"/> <field name="duration"/> <field name="seats"/> <!-- 新增 --> <field name="taken_seats" widget="progressbar"/> <!-- 结束 --> </group> </group> <label for="attendee_ids"/>
<tree string="Session Tree"> <field name="name"/> <field name="course_id"/> <!-- 新增 --> <field name="taken_seats" widget="progressbar"/> <!-- 结束 --> </tree> </field> </record>
Default values
任何类型的 field 都可以设置 default value,只需要在定义时,添加 default=x
。这个 x 可以是python 中的 boolean, string, int, float 等。也可以接受一个函数,以recordset 作为参数,返回一个值
name = fields.Char(default="Unknown")user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
Note:
self.env
提供了一系列的借口,用以处理一些东西
self.env.cr
或者self._cr
是database的corsor, 可以通过这个,直接调用SQLself.env.uid
orself._uid
时当前登录用户的数据表 idself.env.user
当前登录用户的 recordself.env.context
orself._context
当前的 context directoryself.env.ref(xml_id)
通过 xml_id 返回 对应的 recordself.env[model_name]
返回 model_name 对应的 空 record
练习 4-2
- 给 start_date 设置默认值, 为 当前时间
- session 中添加 active 字段
openacademy/models.py
_name = 'openacademy.session' name = fields.Char(required=True) start_date = fields.Date(default=fields.Date.today) # 修改 duration = fields.Float(digits=(6, 2), help="Duration in days") seats = fields.Integer(string="Number of seats") active = fields.Boolean(default=True) # 新增 instructor_id = fields.Many2one('res.partner', string="Instructor", domain=['|', ('instructor', '=', True),
openacademy/views/openacademy.xml
<field name="course_id"/> <field name="name"/> <field name="instructor_id"/> <field name="active"/> <!-- 新增 --> </group> <group string="Schedule"> <field name="start_date"/>
注意:
Odoo 有一个內建机制, 将会自动的隐藏掉 active 字段值为 False 的record
Onchange
Odoo onchange 机制主要用于在 client 中的 form view。当某个字段改变时,自动 update form view,不会主动存在database 中。注意,这个主要作用于 客户端
比如:某个model 拥有三个字段 amout, unit_price and price。我们希望在form中修改其它两个字段时, price 的值会自动的改变。为此,提供了一个装饰器 onchange() 来实现这个功能,
<!-- content of form view --><field name="amount"/><field name="unit_price"/><field name="price" readonly="1"/>
# onchange handler@api.onchange('amount', 'unit_price')def _onchange_price(self): # set auto-changing field self.price = self.amount * self.unit_price # Can optionally return a warning and domains return { 'warning': { 'title': "Something bad happened", 'message': "It was very bad indeed", } }
练习 4-3
添加一个针对 字段变化的 onchange, 当form中字段值填写不合法的时候,弹出警告
openacademy/models.py
r.taken_seats = 0.0 else: r.taken_seats = 100.0 * len(r.attendee_ids) / r.seats # 新增 @api.onchange('seats', 'attendee_ids') def _verify_valid_seats(self): if self.seats < 0: return { 'warning': { 'title': "Incorrect 'seats' value", 'message': "The number of available seats may not be negative", }, } if self.seats < len(self.attendee_ids): return { 'warning': { 'title': "Too many attendees", 'message': "Increase seats or remove excess attendees", }, } # 结束
Model constraints
Odoo提供了两种方式来设置自动验证 invariants: Python constraints and SQL constraints
Python constraints 就像普通method一样,只不过需要使用装饰器 constrains(), 不过也是需要指明 record中的那些字段需要使用 constraint,如果不满足要求,method应该 raise an exception
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)
练习 4-4
添加一个限制条件,用以明确,instructor not in the attendees in his/her own session
openacademy/models.py
# -*- coding: utf-8 -*-from openerp import models, fields, api, exceptions # 修改class Course(models.Model): _name = 'openacademy.course'
'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 constraints 是通过给 model 设置属性 _sql_constraints
, 这个也和domain一样是由一堆 三个元素组成的 tuple 的 list集合。(name, sql_definition, message),name 就是这个 SQL constraint 的名字,sql_definition 就是 table_constraint, message 就是 error message。
练习 4-5
- CHECK course 的title 和 description 是否一样
- 明确 course name 是 unique
openacademy/models.py
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'
- 建立一个Odoo Module (四)- Computed fields、Model constriants
- 建立一个Odoo Module (二)- Basic views、Relational fields
- odoo orm Computed fields
- 建立一个Odoo Module (一)
- 建立一个Odoo Module (三)- Inheritance
- 建立一个Odoo Module (六)- Workflows
- 建立一个Odoo Module (八)- Reporting
- 建立一个Odoo Module (五)- Advanced Views
- 建立一个Odoo Module (七)- Security、Wizard
- Odoo Module (一)
- odoo之模块字段(Fields)
- Odoo ORM API(五)- Fields
- Odoo建立Model(模型)及Form、Tree视图(示例)
- Odoo ORM API(三)- Model Reference
- Odoo Views (四) - Search
- Drupal Module – Computed Field
- kernel module编程(一):建立一个小例子
- Odoo运行机制(四)----URL路由
- Java并发编程:深入剖析ThreadLocal
- Android常用名令集锦(图文并茂)!
- java_(2)基础知识之形参作用域
- 如何在 Git 里撤销(几乎)任何操作
- 洛谷 1313
- 建立一个Odoo Module (四)- Computed fields、Model constriants
- 面向对象程序设计上机练习十一(运算符重载)
- iOS开发之直播App流程介绍
- win7 caffe使用笔记——solver求解器参数以及损失函数的使用
- Android仿Instagram图片加载策略(模糊图片占位+下载进度条)
- Android Preference置灰显示
- Android应用开发中Activity类的用法
- 装饰者模式
- Eclipse中下载SVN插件以及SVN的实际操作