Odoo10開發(fā)教程五(計算字段和默認值)

計算字段和默認值

到目前為止难衰,我們接觸的字段都是存儲在數(shù)據(jù)庫中并直接從數(shù)據(jù)庫檢索钦无。字段也可以通過計算獲得。在這種情況下盖袭,字段的值不是直接檢索自數(shù)據(jù)庫失暂,而是通過調(diào)用模型的方法來實時計算獲得。要創(chuàng)建計算字段鳄虱,需要設(shè)置它的compute屬性為方法名弟塞。這個計算方法通過計算self的每條記錄來設(shè)置字段的值。

注意
self是一個記錄的有序集合拙已,它支持標(biāo)準(zhǔn)的Python集合操作决记,如len(self)iter(self),加上額外的集合操作recs1 + recs2悠栓。迭代過程逐個提供self記錄霉涨,其中每個記錄本身是大小為1的集合。你可以通過點記號來訪問/分配單個記錄上的字段record.name惭适。

import random
from odoo import models, fields, api

class 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))

依賴

計算字段的值通常取決于所在記錄行的其它字段的值笙瑟。ORM層期望開發(fā)人員使用depends()裝飾器來指定計算方法的依賴性。當(dāng)某些依賴關(guān)系被修改后癞志,ORM層通過給定的依賴關(guān)系來觸發(fā)字段的重新計算往枷。

from odoo import models, fields, api

class 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

練習(xí)計算字段

  • 加入座席占用百分比字段到授課模型。
  • 在列表視圖和表單視圖中顯示這個字段
  • 以進度條的方式顯示這個字段

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=x,x可以是Python字面值(bool,int戒突,float屯碴,string),也可以是一個有返回值的方法膊存。

name = fields.Char(default="Unknown")
user_id = fields.Many2one('res.users', default=lambda self: self.env.user)

注意
self.env 對象給出了訪問請求參數(shù)和其他有用的信息:

  • self.env.cr 或者 self._cr是數(shù)據(jù)庫游標(biāo)對象导而,通常用于查詢數(shù)據(jù)庫
  • self.env.uid或者self._uid是當(dāng)前用戶的數(shù)據(jù)庫ID
  • self.env.user是當(dāng)前用戶記錄
  • self.env.ref(xml_id)返回XML ID對應(yīng)的記錄
  • self.env[model_name]返回給定模型的實例

練習(xí)默認值

  • 定義start_date默認值為今天
  • 在授課類添加字段active,并且設(shè)置其默認值為True

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 有內(nèi)置規(guī)則:active字段值為False時記錄不可見

Onchange機制

"onchange"機制為客戶端界面提供了一種方法隔崎,當(dāng)用戶在字段中填寫了值今艺,不需要向數(shù)據(jù)庫保存任何內(nèi)容,就可以更新表單爵卒。例如虚缎,假設(shè)模型有三個字段amount,unit_priceprice,當(dāng)數(shù)量和單價改變時钓株,自動重新計算價格实牡,并在表單界面更新陌僵。要實現(xiàn)這個需求,需要定義一個方法铲掐,并使用onchange()裝飾器拾弃,onchange()的參數(shù)指定了在那個字段改變時,觸發(fā)方法摆霉。其中self代表表單視圖中的記錄,你所做的任何更改奔坟,self都將立刻反應(yīng)在表單上携栋。

<!-- 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",
        }
    }

對于計算字段的值,系統(tǒng)內(nèi)置了onchange()裝飾器咳秉。通過授課表單我們可以觀察到:當(dāng)改變座席數(shù)和參與者人數(shù)婉支,taken_seats進度條會自動更新。

練習(xí)
通過"onchange"機制顯示的驗證無效值澜建,例如座位數(shù)為負數(shù)或者座位數(shù)多與參與者向挖。

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",
                },
            }

模型約束

odoo提供兩種方式實現(xiàn)自動驗證,python constraintssql constraints
Python約束通過方法裝飾器constraints()來定義炕舵,并在記錄集上調(diào)用這個方法何之。裝飾器參數(shù)指定了約束涉及的字段,當(dāng)涉及的字段中任一發(fā)生改變時觸發(fā)方法執(zhí)行咽筋。如果不滿足約束條件溶推,該方法將引發(fā)異常:

from odoo.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

練習(xí)添加Python約束,講師不能在自己的授課出席人中

openacademy/models.py

# -*- coding: utf-8 -*-

from odoo 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約束通過模型屬性_sql_constraints進行定義奸攻。它是一個三元素的元組的列表(name, sql_definition, message)蒜危,其中name是SQL約束名稱,sql_definition是約束規(guī)則睹耐,message是違反約束規(guī)則時的警告信息辐赞。

練習(xí)添加SQL約束,在Postgre SQL文檔幫助下,添加下列約束:

  • 驗證課程描述與課程標(biāo)題不能完全一樣
  • 驗證課程名是唯一的

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'

練習(xí)添加重復(fù)項硝训,因為我們?yōu)檎n程名稱添加了唯一性約束响委,所以不能再使用"復(fù)制"功能(表單->復(fù)制)。重寫"復(fù)制"方法,允許復(fù)制課程對象捎迫,將原始名稱更改為"原始名稱的副本"晃酒。

openacademy/models.py

    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)',
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市窄绒,隨后出現(xiàn)的幾起案子贝次,更是在濱河造成了極大的恐慌,老刑警劉巖彰导,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛔翅,死亡現(xiàn)場離奇詭異敲茄,居然都是意外死亡,警方通過查閱死者的電腦和手機山析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門堰燎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笋轨,你說我怎么就攤上這事秆剪。” “怎么了爵政?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵仅讽,是天一觀的道長。 經(jīng)常有香客問我钾挟,道長洁灵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任掺出,我火速辦了婚禮徽千,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汤锨。我一直安慰自己双抽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布泥畅。 她就那樣靜靜地躺著荠诬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪位仁。 梳的紋絲不亂的頭發(fā)上柑贞,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音聂抢,去河邊找鬼钧嘶。 笑死,一個胖子當(dāng)著我的面吹牛琳疏,可吹牛的內(nèi)容都是我干的有决。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼空盼,長吁一口氣:“原來是場噩夢啊……” “哼书幕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起揽趾,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤台汇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苟呐,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡痒芝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了牵素。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片严衬。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖笆呆,靈堂內(nèi)的尸體忽然破棺而出请琳,到底是詐尸還是另有隱情,我是刑警寧澤腰奋,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布单起,位于F島的核電站,受9級特大地震影響劣坊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屈留,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一局冰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灌危,春花似錦康二、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至味混,卻和暖如春产雹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翁锡。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工蔓挖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人馆衔。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓瘟判,卻偏偏與公主長得像,于是被迫代替她去往敵國和親角溃。 傳聞我的和親對象是個殘疾皇子拷获,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容