【odoo14】應用模型

因爲本章有包含不少基礎知識,我的不會所有轉化爲本身的語言。直接機器翻譯了(用斜體標註,機器翻譯反而一字不落,我會過濾掉冗餘的內容),雖然機翻,但會保證意思不會偏。html

本章主要章節以下:python

  • 定義模型展現及順序
  • 添加字段
  • 配置帶有小數點精度的float型字段
  • 添加貨幣字段
  • 添加關聯字段
  • 添加層級關係
  • 添加模型約束
  • 添加字段字段
  • 在其餘模型中調用關聯字段
  • 使用引用調用關聯信息
  • 使用繼承添加特性
  • 繼承抽象模型複用模型特性
  • 使用委託繼承完整繼承另外一個模型

定義模型展現及順序

模型具備定義其行爲的結構屬性。它們的前綴是
下劃線。模型最重要的屬性是_name,由於它定義了內部全局標識符。在內部,Odoo使用_name屬性來建立一個數據庫表。例如,若是您提供library.book,則Odoo ORM將在數據庫中建立library_book表。這就是爲何在Odoo中,_name屬性必須是惟一的。git

  • _rec_name: 以模型爲單位展現時,模型展現的field字段。
  • _order: 記錄在展現時候的排序

準備

步驟

定義模型文件models/library_book.pysql

  1. 添加模型的描述
_description = 'Library Book'
  1. 添加排序
_order = 'date_release desc, name'
  1. 使用short_name做爲模型的默認展現字段
_rec_name = 'short_name'
short_name = fields.Char('Short Title', required=True)
  1. 在form中添加shot_name字段
<field name="short_name"/>

完整的library_book.py文件以下:數據庫

from odoo import models, fields
class LibraryBook(models.Model):
	_name = 'library.book'
	_description = 'Library Book'
	_order = 'date_release desc, name'
	_rec_name = 'short_name'
	name = fields.Char('Title', required=True)
	short_name = fields.Char('Short Title', required=True)
	date_release = fields.Date('Release Date')
	author_ids = fields.Many2many('res.partner', string='Authors')

完整的form視圖內容以下:編程

<form>
	<group>
		<group>
			<field name="name"/>
			tags"/>
			<field name="author_ids" widget="many2many_
		</group>
		<group>
			<field name="short_name"/>
			<field name="date_release"/>
		</group>
	</group>
</form>

經過UI更新模塊
api

或者經過命令行升級安全

python3 odoo-bin -u my_library

原理

第一步是爲模型的定義添加一個更加用戶友好的標題。這不是強制性的,但能夠由某些附加組件使用。例如,在建立新記錄時,郵件附加模塊中的跟蹤功能將其用於通知文本。有關更多詳細信息,請參閱第23章,在Odoo中管理電子郵件。若是您不使用模型的描述,在這種狀況下,Odoo將在日誌中顯示警告。
默認狀況下,Odoo使用內部id值(自動生成的主鍵)對記錄進行排序。可是,這能夠更改,這樣咱們就可使用咱們選擇的字段,方法是提供一個帶有字符串的_order屬性,該字符串包含以逗號分隔的字段名列表。
字段名後面能夠跟desc關鍵字,以便按降序排序。
數據結構

小貼士
只有存儲在數據庫中的field才能進行排序,對於computed的字段是不支持排序的。app

_order是縮減版的SQL ORDER BY,他不支持NULLS FIRST等。
模型記錄從其餘記錄引用時使用表示。例如,值爲1的用戶標識字段表示管理員用戶。在窗體視圖中顯示時,Odoo將顯示用戶名,而不是數據庫ID。簡而言之,_rec_name是Odoo GUI用來表示該記錄的記錄的顯示名稱。默認狀況下,使用名稱字段。實際上,這是_rec_name屬性的默認值,這就是爲何在咱們的模型中有一個name字段比較方便的緣由。在咱們的例子中圖書館.bookmodel有一個name字段,所以,默認狀況下,Odoo將使用它做爲顯示名。咱們想在步驟3中更改此行爲;咱們使用了short_name做爲_rec_name。以後,library.book模型的顯示名從form視圖的名稱name改成short_name,Odoo GUI將使用short_name的值來表示記錄。

警告
若模型中沒有name字段也沒有配置_rec_name,那麼將展現記錄的模型名稱及記錄ID(library.book, 1)

咱們新增了short_name的字段,實際上是在數據庫表中新增了一列,同時咱們須要在視圖中展現相應的字段。

更多

自odoo8以後,計算字段magic_display字段被默認添加到模型中。他的值是經過nage_get()的模型方法生成的。
name_get()方法默認是經過_rec_name屬性去生成展現的名稱的。若是你想本身實現展現的名稱,能夠重寫name_get()函數。函數必須返回包含由記錄ID和Unicode字符串組成的元組的列表。
舉例:

def name_get(self):
	result = []
	for record in self:
		rec_name = "%s (%s)" % (record.name, record.date_ release)
		result.append((record.id, rec_name))
	return result

添加數據字段

準備

步驟

仍是my_library模塊爲例,models/library_book.py定義了基本的模型。

  1. 增量代碼以下
from odoo import models, fields
class LibraryBook(models.Model):
	# ...
	short_name = fields.Char('Short Title')
	notes = fields.Text('Internal Notes')
	state = fields.Selection(
		[('draft', 'Not Available'),
		('available', 'Available'),
		('lost', 'Lost')],
		'State')
	description = fields.Html('Description')
	cover = fields.Binary('Book Cover')
	out_of_print = fields.Boolean('Out of Print?')
	date_release = fields.Date('Release Date')
	date_updated = fields.Datetime('Last Updated')
	pages = fields.Integer('Number of Pages')
	reader_rating = fields.Float(
	'Reader Average Rating',
	digits=(14, 4), # Optional precision decimals,
	)
  1. 添加對應的視圖
<form>
	<group>
		<group>
			<field name="name"/>
			tags"/>
			<field name="author_ids" widget="many2many_
			<field name="state"/>
			<field name="pages"/>
			<field name="notes"/>
		</group>
		<group>
			<field name="short_name"/>
			<field name="date_release"/>
			<field name="date_updated"/>
			avatar"/>
			<field name="cover" widget="image" class="oe_
			<field name="reader_rating"/>
		</group>
	</group>
	<group>
		<field name="description"/>
	</group>
</form>

下面的代碼定義了字段經常使用的屬性,能夠先有個印象

short_name = fields.Char('Short Title',translate=True, index=True)
state = fields.Selection(
	[('draft', 'Not Available'),
	('available', 'Available'),
	('lost', 'Lost')],
	'State', default="draft")
description = fields.Html('Description', 	sanitize=True, strip_ style=False)
pages = fields.Integer('Number of Pages',
	groups='base.group_user',
states={'lost': [('readonly', True)]},
	help='Total book page count', company_dependent=False)

原理

  • Char: 字符串類型
  • Text: 跨行字符串類型
  • Selection: 選擇列表類型。

重要提醒
Selection類型是可使用數字做爲key的,可是0在odoo中是做爲未設置當前字段存在的。所以,若是key中使用了0,那麼可能存在乎想不到的坑。

  • Html跟text相似,可存儲HTML的富文本內容。
  • Binary: 能夠存儲圖片及文檔。
  • Boolean: True/False
  • Date: 存儲日期。可以使用fields.Date.today()做爲默認值。
  • Datetime: 是做爲navi的UTC時間存儲(不包含時區信息)。可經過fields.Date.now()設置默認值。
  • Integer: 整型。
  • Float: 浮點型。可設置小數點精度。
  • Monetary: 貨幣類型。
    在定義字段的時候,除了以上的類型外,還包含了一些屬性。
  • string: 是字段展現的名稱,若是沒有設置,則會取字段名稱(將_替換爲空格)。
  • translate: 設置爲true,說明該字段是可翻譯的。
  • default: 設置默認值。能夠是一個具體的值,也能夠是一個函數。default=_compute_default。
  • help: 是該字段在頁面以tooltips展現的幫助信息
  • groups: 該字段隸屬於哪些權限組。若是沒有,則默認跟隨模型權限組。
  • states: states容許用戶界面根據state字段的值動態設置readonly、required和invisible屬性的值。所以,它須要一個狀態字段存在並在表單視圖中使用(即便它是不可見的)。state屬性的名稱在Odoo中是硬編碼的,不能更改。
  • copy: 字段的值是否跟隨記錄的copy到新的記錄。對於非關係型字段及many2one的字段,默認是True;對於One2many字段默認是False。
  • index: 設置爲True,將在數據庫表中建立該字段的鍵,可加快搜索。
  • readonly: UI頁面上該字段表現爲只讀。
  • required: 該字段是當前模型必備字段。
  • company_operator標誌位表示爲不一樣的公司存儲不一樣的值。
  • group_operator: 當記錄以相似於sql中的group by進行操做時,該字段經過哪一種運算方式計算結果。經常使用的運算方式有count, count_distinct, array_agg, bool_and, bool_or, max, min, avg, and sum。Integer、float及貨幣類型默認採用sum計算方式。
  • sanitize:用於HTML字段,用於清除html可能包含的不安全標識。
    若是你想進一步控制HMTL的清洗,可經過以下屬性:
  • sanitize_tags=True,移除不屬於白名單的標籤。(白名單定義在odoo/tools/ mail.py)
  • sanitize_attributes=True, 移除不屬於白名單的屬性。
  • sanitize_style=True, 移除不屬於白名單的樣式。
  • strip_style=True, 移除全部的樣式。
  • strip_class=True, 移除全部的類屬性。

更多

添加float字段(配置小數點)

添加貨幣字段

添加關聯字段

添加層級結構

添加約束驗證

添加計算字段

展現存儲在其餘模型中的關聯字段

經過關聯字段添加動態關係

對於關係字段,咱們須要事先肯定關係的目標模型(或協同模型)。然而,有時,咱們可能須要把這個決定留給用戶,首先選擇咱們想要的模型,而後選擇咱們想要連接到的記錄。這能夠經過使用參考字段來實現。

準備

步驟

  1. 編輯models/library_book.py文件
from odoo import models, fields, api
class LibraryBook(models.Model):
	# ...
	@api.model
	def _referencable_models(self):
		models = self.env['ir.model'].search([
	('field_id.name', '=', 'message_ids')])
		return [(x.model, x.name) for x in models]
  1. 添加引用字段
ref_doc_id = fields.Reference(	selection='_referencable_models',
string='Reference Document')

原理

引用字段與m2o字段類似,他們都容許用戶本身選擇關聯的模型。
目標模型經過selection屬性進行選擇,selection必須是包含兩個元素元組的列表,第一個元素是內部標識,第二個標識是展現的內容。
例如:

[('res.users', 'User'), ('res.partner', 'Partner')]

可是,咱們可使用最多見的模型,而不是提供固定的列表。爲了簡單起見,咱們使用了全部具備消息傳遞功能的模型。使用可引用的模型方法,咱們動態地提供了一個模型列表。
咱們的方法是經過提供一個函數來瀏覽全部能夠被引用的模型記錄,從而動態地構建一個將提供給selection屬性的列表。雖然這兩種形式都是容許的,可是咱們在引號中聲明瞭函數名,而不是直接引用不帶引號的函數。這是更靈活的,它容許引用的函數只在稍後的代碼中定義,例如,這在使用直接引用時是不可能的。

函數運行在模型層面,所以須要用@api.model裝飾器。

雖然這個特性看起來不錯,但它會帶來很大的執行開銷。顯示大量記錄的引用字段(例如,在列表視圖中)會形成沉重的數據庫負載,由於每一個值都必須在單獨的查詢中查找。與常規關係字段不一樣,它也沒法利用數據庫引用完整性。

經過繼承爲模型添加新特性

odoo能夠實現對歸屬於其餘模塊的模型功能進行擴展,而不去動原有的代碼。能夠添加字段、方法、以及修改以存在的字段、方法。
odoo提供了三種方式的繼承

  • 類的繼承
  • 原型繼承
  • 委託繼承

準備

步驟

  1. 咱們將爲respartner用戶添加關聯的圖書
class ResPartner(models.Model):
	_inherit = 'res.partner'
	_order = 'name'
	authored_book_ids = fields.Many2many(
	'library.book', string='Authored Books')
	count_books = fields.Integer( 'Number of Authored Books',
	compute='_compute_count_books' )
  1. 添加新增字段的計算函數
# ...
from odoo import api  # if not already imported
# class ResPartner(models.Model):
	# ...
	@api.depends('authored_book_ids')
	def _compute_count_books(self):
		for r in self:
			r.count_books = len(r.authored_book_ids)

原理

咱們經過_inherit屬性實現對於已有模塊的繼承。新增的字段將直接體如今原有模型上。
已有字段也能夠進行增量修改。能夠對原模型中的函數進行重寫或修改(可經過super調用原有模型的函數)。

經過繼承實現模型的copy

原型繼承,對現有模塊完整的複製。

準備

步驟

原型繼承會用到_name及_inherit的類屬性。

  1. 添加library_book_copy.py文件。
  2. 編輯文件
from odoo import models, fields, api
class LibraryBookCopy(models.Model):
	_name = "library.book.copy"
	_inherit = "library.book"
	_description = "Library Book's Copy"
  1. 添加新文件引用, models/init.py

原理

在使用_name及_inherit的類屬性的時候,odoo將使用_name做爲類名複製_inherit的模型。
新的模型將體如今數據庫中,有單獨的數據庫表。以上爲例,library_book_copy表。
原型繼承是copy父類完整的內容,包括字段、屬性及方法。若是將要調整這些內容,可直接定義便可。例如, library.book已經有了name_get函數,可是不符合咱們的要求。咱們能夠在library.book.copy模型中直接新增一個name_get函數。

警告
若是_name使用了父類的名稱,那麼原型繼承是不生效的,而是普通的繼承。

更多

雖然官方提供了原型繼承,可是應用場景不多。反而是經過委託繼承,能夠在不復制整個數據結構的狀況下實現咱們想要的功能。

使用委託繼承實現複製另外一個模型的特性

委託繼承使用類屬性_inherits,注意多了一個s。在某些場景下,相較於修改現有模型,建立一個新的模型並與老的模型進行關聯反而是更好的選擇。
委託繼承與面向對象編程的理念更爲貼近。它還支持多態繼承,便可以同時從多個模型繼承。
好比,咱們有一個圖書館。會有不少的讀書人來圖書館讀書,這些人在咱們這有一些基本的信息(姓名、電話等),其中又有一些人是圖書館會員。會員與普通用戶都有姓名、電話等基礎信息,可是又多了辦理會員的日期、會員卡號等特有信息。

準備

步驟

  1. 添加新的模型, res.partner
class LibraryMember(models.Model):
	_name = 'library.member'
	_inherits = {'res.partner': 'partner_id'}
	partner_id = fields.Many2one(
	'res.partner',
	ondelete='cascade')
  1. 添加member特有的字段
# class LibraryMember(models.Model):
	# ...
	date_start = fields.Date('Member Since')
	date_end = fields.Date('Termination Date')
	member_number = fields.Char()
	date_of_birth = fields.Date('Date of birth')

原理

咱們經過_inherits實現對res.partner對象的委託繼承,這是一個key-value的字典。key是繼承模型的類名,value是當前模型關聯到繼承模型的字段。
當咱們對新模型建立記錄時,會如今res.partner、library.member中分別建立一條記錄,並經過partner_id進行關聯。
委託繼承只是對字段的繼承,並不包含函數。

更多

關於委託繼承,有個簡寫方式。即在m2o中添加delegate=True屬性,去掉_inherits的類屬性。上面的例子能夠寫成

class LibraryMember(models.Model):
	_name = 'library.member'
	partner_id = fields.Many2one('res.partner', ondelete='cascade', delegate=True)
	date_start = fields.Date('Member Since')

一個典型的委託繼承是用戶模型,res.users,繼承自res.partner。也就是説咱們在res.users中看到的一些字段實際上是partner中的。
傳統繼承和原型繼承都是能夠爲模型添加新的特性,可是效率偏低。

使用抽象模型複用模型特性

有時咱們有一個特性,向同時添加到好幾個模型中。抽象模型是能夠實現咱們想要的特性,而後被其餘幾個模型繼承。
舉個例子,咱們將實現一個簡單的歸檔特性。它將活動字段添加到模型中(若是它還不存在),並提供一個存檔方法來切換活動標誌。這是由於活動是一個魔法場。若是默認狀況下存在於模型中,則active=False的記錄將從查詢中過濾掉。

準備

步驟

歸檔特性通常會有本身的模塊,至少是本身的python文件。此處爲了簡單,就放在library_book.py文件中。

  1. 添加虛擬類
class BaseArchive(models.AbstractModel):
	_name = 'base.archive'
	active = fields.Boolean(default=True)
	def do_archive(self):
		for record in self:
			record.active = not record.active
  1. 編輯library.book類以繼承archive模型。
class LibraryBook(models.Model):
	_name = 'library.book'
	_inherit = ['base.archive']
	# ...

原理

抽象模型基於models.AbstractModel建立。他與普通的models.Model功能基本相似,只是他並不在數據庫中建立相應的數據表。他的存在就是爲了讓其餘模型繼承用的。
當一個模型定義了_inherit屬性,那麼他將繼承該收藏模型全部的字段、屬性及方法。
注意,此處_inherit的值是列表。
其實,_inhiret有兩種形式。列表表明繼承自多個模型,單獨的字符串是繼承自一個模型。

更多

最值得一提的抽象模型是mail.thread,它定義在mail(Discuss)模塊中。它爲模型添加了討論的特性。咱們能夠在模型form視圖下方看到消息。 還有一個模型,models.TransientModel。它跟model.Model相似,只是它存儲的數據是暫時的,odoo會有定時任務清理掉。拋掉這個不一樣,瞬態模型跟常規模型同樣。 瞬態模型在用戶交互比較複雜的場景下比較有幫助,好比wizards(嚮導)。將在第八章詳細介紹。

相關文章
相關標籤/搜索