全書完整目錄請見:Odoo 12開發(fā)者指南(Cookbook)第三版
本章中罗岖,我們將講解如下小節(jié):
- 使用外部ID和命名空間
- 使用XML文件加載數(shù)據(jù)
- 使用noupdate和forcecreate標(biāo)記
- 使用CSV文件加載數(shù)據(jù)
- 插件更新和數(shù)據(jù)遷移
- 從XML文件中刪除記錄
- 從XML文件中調(diào)用函數(shù)
引言
本章中,我們將學(xué)習(xí)如何添加在安裝時(shí)可提供數(shù)據(jù)的插件模塊。這對于提供默認(rèn)值以及添加視圖描述切端、菜單或動作等元數(shù)據(jù)都非常有用。另一個(gè)重要的用途是提供演示數(shù)據(jù)群井,勾選了加載演示數(shù)據(jù)復(fù)選框時(shí)會數(shù)據(jù)庫創(chuàng)建的同時(shí)載入數(shù)據(jù)鸥诽。
技術(shù)準(zhǔn)備
本章的技術(shù)要求包含在線Odoo平臺。
本章中的所有代碼可通過GitHub代碼倉庫進(jìn)行下載:https://github.com/PacktPublishing/Odoo-12-Development-Cookbook-Third-Edition/tree/master/Chapter07琴拧。
實(shí)時(shí)代碼操作請觀看如下視頻:http://t.cn/Ailakrsh
使用外部ID和命名空間
本書中到此為上降瞳,我們在視圖、菜單和動作等區(qū)域使用了XML ID蚓胸。但并沒有學(xué)習(xí)XML ID實(shí)際是什么挣饥。這一節(jié)會帶給你更深的理解。
如何操作...
我們將在已有記錄中進(jìn)行寫入來演示如何使用跨模塊引用:
- 在你的模塊聲明文件中添加數(shù)據(jù)文件:
'data': [
'data/data.xml',
],
- 修改主公司的名稱:
<field name="name">Packt publishing</field>
</record>
- 設(shè)置主公司的成員作為出版商:
<field name="publisher_id" ref="base.main_partner" />
</record>
在安裝這一模塊時(shí)沛膳,公司會被重命名并且下一部分中的圖書會被分配給成員扔枫。在該模塊的后續(xù)更新中,只會分配出版商锹安,但公司名稱不做修改短荐。
運(yùn)行原理...
XML ID是一個(gè)引用數(shù)據(jù)庫中某記錄的字符串倚舀。ID本身是ir.model.data模型中的記錄。這一模型的行包含聲明XML ID標(biāo)識字符串和引用ID的模塊忍宋。每次我們寫入XML ID時(shí)痕貌,Odoo會檢查該字符串是否帶有命名空間(即它是否只包含一個(gè)點(diǎn)標(biāo)記),如果不帶有糠排,它會添加當(dāng)前模塊名作為命名空間舵稠。然后,它查找ir.model.data中是否有指定名稱的記錄入宦。若有哺徊,所列出字段會執(zhí)行一個(gè)UPDATE語句。若沒有乾闰,則執(zhí)行一條CREATE語句落追。這樣你可以像前面那樣在記錄已存在時(shí)添加部分?jǐn)?shù)據(jù)。
小貼士:在其它模塊中定義對記錄做修改外涯肩,部分?jǐn)?shù)據(jù)的一個(gè)廣泛應(yīng)用是使用快捷元素來以便捷的方式來創(chuàng)建記錄并在記錄中寫入字段轿钠,而快捷元素并不提供支持:
<act_window id="my_action" name="My action" model="res.partner" />
> <record id="my_action" model="ir.actions.act_window">
> <field name="auto_search" eval="False" />
> </record>
ref函數(shù),在本章中使用XML文件加載數(shù)據(jù)一節(jié)還會使用到宽菜,如果xml文件中的ref屬性值字符串未指定所在模塊谣膳,則在當(dāng)前模塊記錄的XML ID中搜索,如找不到則報(bào)錯(cuò)铅乡。如果XML記錄的id屬性沒有指定所在模塊继谚,則生成記錄時(shí)會自動加上當(dāng)前模塊名作為命名空間。
擴(kuò)展知識...
遲早你會需要在代碼中通過XML ID訪問記錄阵幸。這種情況下使用self.env.ref()函數(shù)花履。這返回所引用記錄的瀏覽記錄。注意在這里你要保持傳入完整的XML ID挚赊。一個(gè)完整XML ID的示例為:<module_name>.<record_id>诡壁。可以在用戶界面中看到任意記錄的XML ID荠割。但需要激活Odoo的開發(fā)者模式妹卿。
參見第一章 安裝Odoo開發(fā)環(huán)境來在 Odoo 中激活開發(fā)者模式。在激活了開發(fā)者模式之后蔑鹦,打開你希望查看XML ID的記錄的表單視圖夺克。會在頂欄中看到一個(gè)調(diào)試圖標(biāo)。在該菜單中嚎朽,點(diǎn)擊View Metadata選項(xiàng)铺纽。參見如下截圖:
TODO
其它內(nèi)容
參見本章中使用noupdate和forcecreate標(biāo)記一節(jié)來了解為何公司名僅在模塊安裝時(shí)才會進(jìn)行修改。
使用XML文件加載數(shù)據(jù)
使用第五章 應(yīng)用模型中的數(shù)據(jù)模型哟忍,我們將添加一本書和一位作者來作為演示數(shù)據(jù)狡门。我們還會添加一個(gè)知名的出版社來作為模塊中的常規(guī)數(shù)據(jù)陷寝。
如何操作...
創(chuàng)建兩個(gè)XML文件并在manifest.py文件中關(guān)聯(lián)它們。
- 在你的聲明文件中的demo版塊中添加一個(gè)名為 data/demo.xml的文件:
'demo': [
'data/demo.xml',
],
- 在該文件中添加如下內(nèi)容:
<odoo>
<record id="author_pga" model="res.partner">
<field name="name">Parth Gajjar</field>
</record>
<record id="author_af" model="res.partner">
<field name="name">Alexandre Fayolle</field>
</record>
<record id="author_dr" model="res.partner">
<field name="name">Daniel Reis</field>
</record>
<record id="author_hb" model="res.partner">
<field name="name">Holger Brunn</field>
</record>
<record id="book_cookbook" model="library.book">
<field name="name">Odoo Cookbook</field>
<field name="short_name">cookbook</field>
<field name="date_release">2016-03-01</field>
<field name="author_ids"
eval="[(6, 0, [ref('author_af'),
ref('author_dr'),
ref('author_hb')])]"
/>
<field name="publisher_id" ref="res_partner_packt" />
</record>
</odoo>
- 在聲明文件的data版塊中添加一個(gè)名為data/data.xml 的文件:
'data': [
'data/data.xml',
...
],
- 在data/data.xml 文件中加入如下 XML 內(nèi)容:
<odoo>
<record id="res_partner_packt" model="res.partner">
<field name="name">Packt Publishing</field>
<field name="city">Birmingham</field>
<field name="country_id" ref="base.uk" />
</record>
</odoo>
此時(shí)若更新該模塊其馏,會看到我們所創(chuàng)建的出版社凤跑,并且如果像第四章 創(chuàng)建Odoo插件模塊中所指出的那樣啟用了演示數(shù)據(jù),還可以看到這本書以及其作者叛复。
運(yùn)行原理...
演示數(shù)據(jù)在技術(shù)上普通數(shù)據(jù)相同饶火。不同之外在于前者通過聲明文件中的 demo 鍵拉取數(shù)據(jù),后者通過 data 鍵來拉取數(shù)據(jù)致扯。使用<record>元素來創(chuàng)建記錄,它有一個(gè)必填的id和model屬性当辐。對于 id 屬性抖僵,參見使用外部ID和命名空間一節(jié),model屬性引用模型的_name屬性缘揪。然后耍群,使用<field>元素來填入數(shù)據(jù)庫中列,這在你所命名的模型中定義找筝。模型還決定哪些字段是必填的并且有可能定義了默認(rèn)值蹈垢。在這種情況下,你無需顯式地給這些字段賦值袖裕。在第2步中曹抬,<field>元素可以在標(biāo)量值的情況下包含簡單文本值。如果你需要傳遞一個(gè)文件的內(nèi)容(例如設(shè)置圖片時(shí))急鳄,使用<field>元素的file屬性并傳遞相對插件路徑的文件名谤民。
設(shè)置引用有兩種方式。最簡單的一種是使用ref屬性疾宏,可用于many2one字段张足,僅需包含所引用記錄的XML ID。對于one2many和many2many字段坎藐,我們需要使用eval屬性为牍。這是一種通用目的屬性,可用于運(yùn)行Python代碼來作為字段值使用岩馍,例如使用strftime('%Y-01-01')來填充date字段碉咆。X2many中應(yīng)使用三個(gè)元素元組列表,元組的第一個(gè)值決定所要采取的操作兼雄。在eval屬性內(nèi)吟逝,我們可以訪問一個(gè)名為ref的函數(shù),它返回以字符串傳入的XML ID 對應(yīng)的數(shù)據(jù)庫ID赦肋。這讓我們可以無需知道不同數(shù)據(jù)庫中可能不同的原始 ID 即可引用記錄块攒,如下所示:
- (2, id, False):它會刪除通過數(shù)據(jù)庫 id 所關(guān)聯(lián)的記錄励稳。元組的第三個(gè)元素被忽略。
- (3, id, False):它從one2many字段通過id,取消記錄的關(guān)聯(lián)囱井。注意這個(gè)操作不會刪除原記錄驹尼,已有記錄保持原樣。元組的最后一個(gè)元素也被忽略庞呕。
- (4, id, False):它添加一個(gè)已有記錄 id的關(guān)聯(lián)新翎,元組的最一個(gè)元素被忽略。這應(yīng)該是你會最常使用到的住练,通常伴隨ref函數(shù)來通過其XML ID來獲取記錄的數(shù)據(jù)庫 ID地啰。
- (5, False, False):它會切斷所有關(guān)聯(lián),但仍保持所關(guān)聯(lián)的記錄的完整性讲逛。
- (6, False, [id, ...]):它會清除當(dāng)前引用的記錄來將它們替換為 ID 列表中包含的記錄亏吝。元組的第二個(gè)元素被忽略。
??注意數(shù)據(jù)文件中的順序很重要盏混,并且數(shù)據(jù)文件中的記錄僅能引用該文件此前列表中所定義的記錄蔚鸥。這也是為什么你需要保持查看模塊是否在空數(shù)據(jù)庫中安裝,因?yàn)樵陂_發(fā)過程中许赃,你會經(jīng)常在各處添加記錄止喷,能夠使用的前提是后續(xù)所定義記錄通過前幾次更新已存在于數(shù)據(jù)庫中。演示數(shù)據(jù)總是會通過data鍵后的文件進(jìn)行載入混聊,這也是本例中的引用可以生效的原因弹谁。
擴(kuò)展知識...
雖然基本上你可以對記錄元素做任何操作,開發(fā)人員可以使用一些快捷元素來便捷地創(chuàng)建某種類型的記錄技羔。這包含menu item, template和 act window僵闯。參見第十章 后端視圖和第十六章 網(wǎng)頁客戶端開發(fā)來獲取更多相關(guān)信息。
一個(gè)字段元素還可以包含function元素藤滥,它調(diào)用模型中的方法來提供字段值鳖粟。參見從XML文件中調(diào)用函數(shù)一節(jié),其中我們僅通過調(diào)用函數(shù)來在應(yīng)用中直接繞過加載機(jī)制并寫入數(shù)據(jù)庫拙绊,
前述列表中沒有包含0和1向图,因?yàn)樵谳d入數(shù)據(jù)庫這兩者并沒有什么用處。為保持完整性标沪,說明如下:
- (0, False, {'key': value}):它會新建所引用模型的記錄榄攀,字段值由第3個(gè)位置參數(shù)的字典填寫。元組的第二個(gè)元素被忽略金句。因?yàn)檫@些元素不包含XML ID并在每次更新模塊時(shí)運(yùn)行檩赢,會導(dǎo)致重復(fù)數(shù)據(jù),最好避免使用违寞。取代方案是在其自身記錄元素中創(chuàng)建記錄贞瞒,并按照如何操作一節(jié)講解的進(jìn)行關(guān)聯(lián)偶房。
- (1, id, {'key': value}):它可用于寫入已關(guān)聯(lián)的記錄。由于上述的同樣原因军浆,應(yīng)避免在你的XML文件中使用這種語法棕洋。
這些語法與我們在第六章 基本服務(wù)端部署新建記錄和更新記錄集中記錄值小節(jié)中所講解的相同。
使用noupdate和forcecreate標(biāo)記
大部分的插件模塊擁有不同類型的數(shù)據(jù)乒融。有些數(shù)據(jù)需要存在來讓模塊正常運(yùn)作掰盘,另一些數(shù)據(jù)不應(yīng)由用戶修改,大部分?jǐn)?shù)據(jù)都可以供用戶修改赞季,僅出于方便目的予以提供愧捕。本節(jié)會詳細(xì)講解這些不同類型的數(shù)據(jù)。首先申钩,我們將在已有記錄中寫入一個(gè)字段晃财,然后我們會創(chuàng)建一個(gè)在模塊更新時(shí)會被創(chuàng)建的記錄。
如何操作...
我們可以通過在 Odoo 中加載數(shù)據(jù)時(shí)設(shè)置<odoo>元素或<record>元素自身的某些屬性來施加不同的行為典蜕,
- 添加在安裝時(shí)會創(chuàng)建但后續(xù)更新不同更新的出版商。但在用戶刪除它時(shí)會被重新創(chuàng)建:
<record id="res_partner_packt" model="res.partner">
<field name="name">Packt publishing</field>
<field name="city">Birmingham</field>
<field name="country_id" ref="base.uk"/>
</record>
</odoo>
- 添加一個(gè)在插件更新時(shí)不會修改且用戶刪除后不會重建的圖書分類:
<record id="book_category_all"
model="library.book.category"
forcecreate="false">
<field name="name">All books</field>
</record>
</odoo>
運(yùn)行原理...
<odoo>元素可包含一個(gè)noupdate屬性罗洗,在 ir.model.data記錄中由第一次讀取所包含的數(shù)據(jù)記錄創(chuàng)建愉舔,因此成為數(shù)據(jù)表中的一個(gè)字段。
在Odoo安裝插件時(shí)(稱為 init 模式)伙菜,不論noupdate為true或false都會寫入所有記錄轩缤。在更新插件時(shí)(稱為update模式),會查看已有的XML ID來確定是否設(shè)置了noupdate標(biāo)記贩绕,如是火的,則會忽略準(zhǔn)備寫入到該XML ID 的元素。在用戶刪除該記錄時(shí)則并非如此淑倾,因此你可以通過在update模式下設(shè)置記錄的forcecreate標(biāo)記為false來強(qiáng)制不重建noupdate記錄馏鹤。
??在老版本的插件中(版本8.0及以前),你經(jīng)常會發(fā)現(xiàn)<openerp>元素中包含一個(gè)<data>元素娇哆,其中又包含<record>及其它元素湃累。這仍然可用,但屬于被淘汰的方式“郑現(xiàn)在治力,<odoo>, <openerp>和<data>的方法完全一致,它們作為XML數(shù)據(jù)的一個(gè)包裹勃黍。
擴(kuò)展知識...
如果在使用noupdate標(biāo)記時(shí)你依然想要加載記錄宵统,可以在運(yùn)行Odoo服務(wù)時(shí)帶上--init=your_addon或-i your_addon參數(shù)。這會強(qiáng)制Odoo重載記錄覆获。這還會重建已刪除的記錄马澈。注意如果模塊繞過了XML ID機(jī)制的話這可能會導(dǎo)致重復(fù)記錄以及關(guān)聯(lián)安裝的報(bào)錯(cuò)瓢省,例如Python代碼中由<function>標(biāo)簽所調(diào)用來創(chuàng)建記錄時(shí)。
通過這一代碼箭券,你可以繞過任意noupdate標(biāo)記净捅,但首先請確保這確實(shí)是你所需要的。另一個(gè)解決這一場景的方案是編寫一個(gè)遷移腳本辩块,參見插件更新和數(shù)據(jù)遷移一節(jié)蛔六。
其它內(nèi)容
Odoo還使用XML ID來追蹤插件升級后所刪除的數(shù)據(jù)。如果記錄在更新前通過模塊命名空間獲取到一個(gè)XML ID废亭,但在更新時(shí)重置了該XML ID国章,記錄會因被視作已過期而從數(shù)據(jù)庫中刪除。有關(guān)這一機(jī)制更深入的討論豆村,請見插件更新和數(shù)據(jù)遷移一節(jié)液兽。
使用CSV文件加載數(shù)據(jù)
雖然可以通過XML文件來進(jìn)行所需任意操作,但在提供大量數(shù)據(jù)時(shí)這種格式并不是很方便掌动,尤其是現(xiàn)在很多人都習(xí)慣于使用Calc或其它數(shù)據(jù)表軟件處理數(shù)據(jù)四啰。CSV格式的另一個(gè)優(yōu)勢在于它是使用標(biāo)準(zhǔn)導(dǎo)出函數(shù)導(dǎo)出數(shù)據(jù)的格式。本節(jié)中粗恢,我們來學(xué)習(xí)導(dǎo)入類表格數(shù)據(jù)柑晒。
如何操作...
最初,訪問控制列表(ACL眷射,參見第十一章 權(quán)限安全)是通過CSV文件加載的一種數(shù)據(jù)類型匙赞。
- 在數(shù)據(jù)文件中添加security/ir.model.access.csv:
'data': [
...
'security/ir.model.access.csv',
],
- 在這個(gè)文件中添加我們圖書的ACL(我們已經(jīng)通過第四章 創(chuàng)建Odoo插件模塊中添加訪問權(quán)限一節(jié)添加了一些記錄):
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
acl_library_book_user,ACL for books,model_library_book,base.group_user,1,0,0,0
現(xiàn)在我們有了一個(gè)ACL,它允許普通用戶讀取圖書記錄妖碉,但無法編輯涌庭、創(chuàng)建或刪除這些記錄。
運(yùn)行原理...
你僅需將所有數(shù)據(jù)文件放到聲明文件的數(shù)據(jù)列表中欧宜。Odoo會通過文件擴(kuò)展名來確定其文件類型坐榆。CSV文件的一個(gè)特別之處在于它必須匹配所導(dǎo)入的模型名稱,本例冗茸,該模型為 ir.model.access猛拴。第一行應(yīng)為精確匹配模型字段名稱的列名的頭部。
對于標(biāo)量值蚀狰,你可以使用帶引號(因字符串自身包含引號或逗號而需要帶上引號)或不帶引號的字符串愉昆。
在通過CSV文件編寫many2one字段時(shí),Odoo首先嘗試將字段值解釋為XML ID麻蹋。如無點(diǎn)號跛溉,Odoo會將當(dāng)前模塊名添加為命名空間,并在 ir.model.data中查找結(jié)果。如若失敗芳室,會使用字段值來作為參數(shù)調(diào)用模型的name_search函數(shù)专肪,獲取第一條返回的結(jié)果。如依然失敗堪侯,該行被視為無效嚎尤,Odoo拋出錯(cuò)誤。
??注意從CSV中所讀取的數(shù)據(jù)保持為noupdate=False伍宦,并且沒有快捷的修改方式芽死。這表示所有后續(xù)的插件更新會一直重寫由用戶所做的修改。
如果你需要要加載大量的數(shù)據(jù)并且noupdate對你來說不是問題次洼,可以在init鉤子中加載CSV文件关贵。
擴(kuò)展知識...
也可通過CSV文件來導(dǎo)入one2many和many2many字段,但會有些麻煩卖毁。通過揖曾,最好是分別創(chuàng)建記錄然后在XML文件中設(shè)置關(guān)聯(lián),或者通過另一個(gè)CSV文件來設(shè)置關(guān)聯(lián)亥啦。
如果你確需在同一個(gè)文件中創(chuàng)建關(guān)聯(lián)記錄炭剪,對數(shù)據(jù)列進(jìn)行排序來讓標(biāo)量字段居左、關(guān)聯(lián)模型字段居右翔脱,列頭包含待關(guān)聯(lián)名稱和關(guān)聯(lián)模型字段念祭,由逗號分隔:
"id","name","model_id:id","perm_read","perm_read", "group_id:name"
"access_library_book_user","ACL for books","model_library_book",1, "my group"
這會創(chuàng)建一個(gè)名為my group的組,你可以通過在組記錄的右側(cè)添加列來寫入更多的字段碍侦。如果需要關(guān)聯(lián)多條記錄,重復(fù)該行并修改右側(cè)列為對應(yīng)值隶糕。因Odoo會為空列填寫前一行的值瓷产,你無需拷貝所有數(shù)據(jù),只需在添加行中為值留空來獲取所要填入的關(guān)聯(lián)模型枚驻。
對于x2m字段濒旦,只需要列出要關(guān)聯(lián)記錄的XML ID。
插件更新和數(shù)據(jù)遷移
在編寫插件模塊時(shí)所選擇的數(shù)據(jù)模型可能會存在一些問題再登,因此會需要在插件模塊的生命周期內(nèi)對其進(jìn)行調(diào)整尔邓。為允許這一操作而又無需過多技巧,Odoo支持插件模塊中使用版本號并在需要時(shí)執(zhí)行遷移锉矢。
如何操作...
我們假定在模塊的早前版本中date_release字段是一個(gè)字符字段梯嗽,人們可以填寫任意他們認(rèn)為符合日期的字段。現(xiàn)在我們意識到我們需要對這一字段進(jìn)行比較和累加沽损,因此需要將它的類型修改為Date灯节。Odoo在類型轉(zhuǎn)換上做了很大的優(yōu)化,但在這種情況下得靠我們自己了,因此我們需要給出指令來對已安裝在數(shù)據(jù)庫中的早前版本進(jìn)行轉(zhuǎn)換來讓當(dāng)前版本可以運(yùn)行:
- 提升manifest.py文件中的版本號:
'version': '12.0.1.0.1',
- 在migrations/12.0.1.0.1/pre-migrate.py中提供預(yù)遷移代碼:
def migrate(cr, version):
cr.execute('ALTER TABLE library_book RENAME COLUMN date_release TO date_release_char')
- 在migrations/12.0.1.0.1/postmigrate.py中添加一個(gè)遷移后代碼:
from odoo import fields
from datetime import date
def migrate(cr, version):
cr.execute('SELECT id, date_release_char FROM library_book')
for record_id, old_date in cr.fetchall():
# check if the field happens to be set in Odoo's internal
# format
new_date = None
try:
new_date = fields.Date.to_date(old_date)
except ValueError:
if len(old_date) == 4 and old_date.isdigit():
# probably a year
new_date = date(int(old_date), 1, 1)
else:
# try some separators, play with day/month/year
# order ...
pass
if new_date:
cr.execute('UPDATE library_book SET date_release=%s', (new_date,))
沒有這代碼炎疆,Odoo會將原來的date_release列重命名為date_release_moved并新建一列卡骂,因?yàn)闆]有字符字段對日期字段的直接自動轉(zhuǎn)換。從用戶的角度看形入,date_release的數(shù)據(jù)會消失全跨。
運(yùn)行原理...
第一個(gè)重要的點(diǎn)是在插件中增添版本編號,因?yàn)檫w移僅在不同版本中進(jìn)行亿遂。在每次更新期間浓若,Odoo在更新時(shí)將聲明文件中的版本號寫入到ir_module_module表中。版本號前綴使用Odoo的大版本和小版本號崩掘,這是一種良好實(shí)踐七嫌,但1.0.1也可以達(dá)到同樣的效果,因?yàn)樵趦?nèi)部苞慢,Odoo會為短版本號添加其大版本和小版本號诵原。通常,使用長標(biāo)記是一種不錯(cuò)的做法挽放,因?yàn)楹苋菀椎乜闯鏊轻槍doo的哪一個(gè)版本的绍赛。
兩個(gè)遷移文件是無需在任何地方注冊的代碼文件。在更新插件時(shí)辑畦,Odoo對比在ir_module_module中記錄的插件聲明文件中所添加的版本號吗蚌。如果聲明文件的版本號更高(在添加了 Odoo 的大版本和小版本之扣),這一插件的遷移文件夾會被搜索來查看它是否包含帶有范圍內(nèi)版本號的文件夾纯出,以及包含當(dāng)前更新的版本蚯妇。
然后,在查找到的文件夾內(nèi)暂筝,Odoo搜索以pre-開頭的Python文件箩言,加載它們,并預(yù)設(shè)其中定義了名為migrate的函數(shù)焕襟,該函數(shù)有兩個(gè)參數(shù)陨收。此函數(shù)調(diào)用時(shí)以數(shù)據(jù)庫游標(biāo)作為第一個(gè)參數(shù)以及當(dāng)前安裝的版本號作為第二參數(shù)。發(fā)生時(shí)間在Odoo查找插件定義了其它代碼之前鸵赖,因此你可以假定你的數(shù)據(jù)庫布局對比此前版本沒有做過任何修改务漩。
在所有預(yù)遷移函數(shù)成功調(diào)用之后,Odoo加載模型以及插件中所定義的數(shù)據(jù)它褪,這會導(dǎo)致數(shù)據(jù)庫布局的變化饵骨。例如我們在pre-migrate.py文件中重命名了date_release,Odoo會以正確的數(shù)據(jù)類型使用該名稱新建一列茫打。
此后宏悦,通過同樣的搜索算法镐确,會搜索遷移后文件并在找到時(shí)進(jìn)行執(zhí)行。在我們的例子中饼煞,我們需要所有的值來了解我們是否能借助它讓一些數(shù)據(jù)可以使用源葫,否則我們會保持?jǐn)?shù)據(jù)為NULL。除非絕對必要不要編寫遍歷整表的腳本砖瞧,在這種情況下息堂,我們可能會編寫一個(gè)大而不可讀的SQL的switch語句。
小貼士:如果你僅僅是想要重命名一列块促,則無需編寫遷移腳本荣堰。在這種情況下,你可以設(shè)置需修改的字段原列名為oldname參數(shù)竭翠,然后Odoo自己處理重命名振坚。
擴(kuò)展知識...
在預(yù)遷移和遷移后的步驟中,僅能訪問到游標(biāo)斋扰,如果你習(xí)慣于使用Odoo環(huán)境則不是很方便渡八。它可能支導(dǎo)致在這一階段使用模型預(yù)期外的結(jié)果,因?yàn)樵陬A(yù)遷移步驟中,插件模型尚未被載入,同時(shí)也糊,在后遷移步驟中,定義依賴當(dāng)前插件的插件模型也還未被加載逮壁。但是,如果這對于你來說不是問題粮宛,可能是因?yàn)槟阆胍褂媚愕牟寮簧婕暗哪P突蛘呤悄阋阎@不會是一個(gè)問題的模型窥淆,那么你可以編寫如下代碼創(chuàng)建一個(gè)習(xí)慣的環(huán)境:
from odoo import api, SUPERUSER_ID
def migrate(cr, version):
env = api.Environment(cr, SUPERUSER_ID, {})
# env holds all currently loaded models
其它內(nèi)容
在編寫遷移腳本時(shí),你常常會碰到重復(fù)的任務(wù)巍杈,比如查某數(shù)據(jù)列或數(shù)據(jù)表是否存在忧饭、重命名或映射一些舊的值到新值。重復(fù)造輪子可能會容易產(chǎn)生問題并讓你沮喪秉氧,如果你可以承擔(dān)額外依賴的話考慮使用https://github.com/OCA/openupgradelib。
從XML文件中刪除記錄
在前一節(jié)中蜒秤,我們學(xué)習(xí)了如何通過XML文件創(chuàng)建或更新記錄汁咏。有時(shí),通過依賴的模塊作媚,你會想要刪除此前創(chuàng)建的記錄攘滩。這可以通過<delete>標(biāo)簽實(shí)現(xiàn)。
準(zhǔn)備工作
在這一節(jié)中纸泡,我們將通過XML文件添加一些分類漂问,然后刪除它們。在真實(shí)場景中,你會從另一個(gè)模塊中創(chuàng)建這一記錄蚤假。但為進(jìn)行簡化栏饮,我們將在相同的 XML 文件中添加一些分類,如下:
<field name="name">Test Category</field>
</record>
<record id="book_category_not_delete" model="library.book.category">
<field name="name">Test Category 2</field>
</record>
如何操作...
有兩種方式從XML文件中刪除記錄:
- 通過此前創(chuàng)建的 XML ID:
<delete model="library.book.category" id="book_category_to_delete"/>
- 通過搜索域:
<delete model="library.book.category" search="[('name', 'ilike', 'Test')]"/>
運(yùn)行原理...
你將需要使用<delete>標(biāo)簽磷仰。要從模型中刪除記錄袍嬉,需要在model屬性中提供模型的名稱。這是一個(gè)必填的屬性灶平。
在第一個(gè)方法中伺通,你需要提供此前從另一個(gè)模塊數(shù)據(jù)文件創(chuàng)建的記錄的XML ID。在模塊的安裝過程中逢享,Odoo會嘗試查找該記錄罐监。如果以給定XML ID,查找到記錄,它會刪除該記錄瞒爬,否則會報(bào)出一個(gè)錯(cuò)誤弓柱。你可以僅刪除通過XML文件創(chuàng)建的記錄(或帶有XML ID的記錄)。
在第二個(gè)方法中疮鲫,你需要在domain屬性中傳遞域吆你。在模塊的安裝過程中,Odoo會通過這一哉搜索記錄俊犯。如果查找到記錄妇多,則進(jìn)行刪除。如果給定域沒有匹配到任何記錄的話該選項(xiàng)不會拋出異常燕侠。使用該選項(xiàng)時(shí)要極其小心者祖,因?yàn)樗阉鬟x項(xiàng)會刪除所有匹配域的記錄,而導(dǎo)致它會刪除用戶的數(shù)據(jù)绢彤。
??在Odoo很少使用<delete>七问,因?yàn)樗芪kU(xiǎn)。如果使用不慎茫舶,可能會導(dǎo)致系統(tǒng)崩潰械巡。盡可能避免使用它。
從XML文件中調(diào)用函數(shù)
你可以通過XML文件創(chuàng)建所有類型的記錄饶氏,但有時(shí)生成包含一些業(yè)務(wù)邏輯的數(shù)據(jù)會很困難讥耗。你可能會想要在用戶在生產(chǎn)環(huán)境中安裝依賴模塊時(shí)修改記錄。例如疹启,假設(shè)你想要創(chuàng)建一個(gè)模塊來在線展示書籍古程。my_library模塊已有圖片封面字段。設(shè)想在新的模塊中你實(shí)現(xiàn)了減小圖像大小以及在新的縮略圖字段中存儲它的邏輯『把拢現(xiàn)在每當(dāng)用戶安裝這一模塊時(shí)挣磨,可能已有有書和圖像了雇逞。不太可能在XML文件中通過<record>標(biāo)簽生成縮略圖。在這種情況下茁裙,你可以通過<function>標(biāo)簽調(diào)用該模型方法塘砸。
如何操作...
對于這一節(jié),我們將使用前一節(jié)的代碼呜达。作為示例谣蠢,我們將已有圖書價(jià)格增加$10 USD。注意你可能根據(jù)公司配置使用其它幣種查近。
如下步驟會通過XML文件喚醒Python方法:
- 在library.book模型中添加_update_book_price()方法:
@api.model
def _update_book_price(self):
all_books = self.search([])
for book in all_books:
book.cost_price += 10
- 在數(shù)據(jù)XML文件中添加<function>:
<function model="library.book" name="_update_book_price"/>
運(yùn)行原理...
在第一步中眉踱,我們添加了update_book_price()方法,它搜索所有圖書并將它們的價(jià)格增加$10 USD霜威。我們在方法名前加上下劃線谈喳,是因?yàn)樗籓RM視作私有,不允許通過RPC調(diào)用戈泼。
在第二步中婿禽,我們使用了<function>標(biāo)簽并添加了兩個(gè)屬性:
- model:在其中聲明方法的模型名稱
- name:你想要調(diào)用的方法名
在你安裝這一模塊時(shí),_update_book_price()會被調(diào)用并且書籍的價(jià)格會被加上$10大猛。
??保持為一函數(shù)添加noupdate選項(xiàng)扭倾。否則,會在每次更新模塊時(shí)調(diào)用它挽绩。
擴(kuò)展知識...
通過<function>可以向函數(shù)發(fā)送參數(shù)膛壹。假設(shè)你只想要在某個(gè)分類中增加圖書的價(jià)格并且通過參數(shù)發(fā)送這一增加的值。
那么唉堪,需要創(chuàng)建一個(gè)方法來接收分類作為一個(gè)參數(shù)模聋,如下:
@api.model
def update_book_price(self, category, amount_to_increase):
category_books = self.search([('category_id', '=', category.id)])
for book in category_books:
book.cost_price += amount_to_increase
傳遞分類和數(shù)值作為參數(shù),需要使用eval屬性唠亚,如下:
<function model="library.book"
name="update_book_price"
eval="(ref('category_xml_id'), 20)"/>
在你安裝這一模塊時(shí)链方,會對指定分類的圖書單價(jià)增加$20。.