Model
現在開始,會進行無氧運動,你必須先有印象,筆者希望讀者在看的時候可以邊做筆記邊記憶,在Odoo當中,Model所扮演的腳色,主要是與DB溝通,也就是CRUD,相信接觸過MVC或是MTV架構的讀者不會陌生,這個功能就是ORM,我們可以透過ORM使的我們在撰寫程式時,規避掉很多資安上的問題,在這邊筆者就不細談,讀者可以透過Google或是ChatGPT去了解,當我們透過撰寫Model,使用ORM來創建DB中的Table,而這些Table的資料,我們在odoo中稱他們為record。
○ (model.Models) - 最常使用的model類型
my_module/init.py 在開始controller設定之前,我們必須先將我們models給import進去from . import models
my_module/models/init.py 在開始controller設定之前,我們必須先將我們models.py給import進去from . import library_model
○ 種類和使用
from odoo import models,fielsd,api
class Library(models.Model):
_name = 'library.book'
_description = 'library.book'
_inherit='mail'
_order='sequence,name'
name
: model的名稱。description
: model檢的的描述。inherit
:繼承自這個model的屬性。order
: 主要是排序,跟SQL中的order by是類似的。
當我們想要呼叫Library當中的欄位資料時,我們可以使用self.env['library.book']
fields.Char()
- 常用屬性有string(代表欄位的名子)跟size(可以設定最長長度)fields.Text()
- 常用屬性為string(代表欄位的名子)fields.Integer()
- 常用屬性為string(代表欄位的名子)fields.Float()
- 常用屬性有string(代表欄位的名子)跟digits(通常會是一個tuple並且分別代表('總位數','小數精度'))fields.Html()
- 常用屬性為string(代表欄位的名子)fields.Date()
- 常用屬性string(代表欄位的名子)或是default=lambda = self:fields.Date.now()
fields.Datetime()
- 常用屬性string(代表欄位的名子)或是default=lambda = self:fields.Datetime.now()
fields.Binary()
- 常用屬性為string(代表欄位的名子)fields.Selection()
- 常用屬性有items(會是一個[list])跟string(代表欄位的名子)。
#Date()
與Datetime()
還有這些類型:
fields.Date.to_date(string_value)
fields.Date.to_string(date_value)
fields.Date.today()
fields.Date.context_today(record, timestamp)
---------------------------------------------
fields.Datetime.to_date(string_value)
fields.Datetime.to_string(datetime_value)
fields.Datetime.today()
fields.Datetime.context_today(record, timestamp)
上面這些都是常用的欄位常用的屬性:
string
: 欄位名稱required
: 必填欄位help
: 提示訊息compute
: 可以對欄位的資料做及時的計算,不會回存到table中但可以透過store=True
來進行儲存。default
: 設定預設值。translate
: 通常在Char
、Text
、Html
中使用,主要使用在翻譯。readonly
: 預設為False
,只可以讀不能編輯index
: 設定為True
之後,會優先被查找減輕DB負載。copy
: 設定該欄位能不能被複製。groups
: 設定只有該群組可以看到欄位。states
: 通常長這樣{'done':['readonly',True]}
,用這樣的方式來設定欄位只能被讀。還可以設定required、invisible。
deprecated
: 只要將其設定為True,會在日誌中記錄警告訊息。active
: 如果降他設定為False,當前端在查詢時將會被忽略。sequence
: 必須在_order
中引入,可以手動定義record順序。related
: 透過關聯把資料拉出來,不會儲存在DB中,與fields.selection()
搭配使用。gender
= fields.Selection('Gender', related='employee_id.gender')
○ @api
常用的@api有以下這些:
@api.model
- 通常會運用在修改既有CRUD函式時會呼叫它,而在odoo16它已經取代了@api.one與@api.multi的功能了。@api.constrains(" ")
- 主要的功能是在約束fields
,與_sql_constraints
是擁有相同的功能。@api.depends(" ")
- 通常與compute一起使用,只能在view上做操作,操作過的record不會儲存在table中。@api.onchange(" ")
- 可以對record資料做計算,但只能在同一model中做使用。
大多數情況,使用@api都是為了DB效能而使用,當你使用方法在抓取recordset的時候你沒有使用@api ,odoo會預設為@api.model
來實行。
內建的CRUD(創建、讀、更新、刪除):
read([])
create({})
write({})
unlink()
browse(int)
※括弧當中所代表的是,填寫資料的屬性如'[]'代表字串、'{}'代表字典、'int'代表整數。
search() 當我們想使用其他model的recordset的時候,我們可以這樣做:
def other_recordset(self):
all_members=self.env['library.member'].search([])
print("All members:",all_members)
return True
或是我們想要創建一個新record:
record=self.evn['library.book.category'].create(parent_category_val)
還可以更新record:
def update_record(self):
self.ensure_one()
self.date_release=fields.Date.today()
------------------------------------------
def update_record(self):
self.ensure_one()
self.update({'date_release':'fields.Date.today()'})
我們也可以透過write({})
來對record的操作
record.write({'child_ids':[0,0,child]})
record.write({'child_ids':[1,child_index,update_child]})
這邊詳細介紹這些( , , )
內所代標的意思
(0,0,{a:b}) - 創建record,並且將{a:b}關聯
(1,id,{a:b}) - 更新id值為id的record,並且將{a:b}關聯
(2,id,) - 刪除id值為id的record
(3,id,) - unlink id值為id的record
(4,id,) - link id值為id的record
(5,0,0) - 刪除所有的 link record
(6,0,[ids]) - 替換現有的 link record
○ 報錯
from odoo.exceptions import UserError
from odoo.tools.translate import _
首先我們要import UserError
跟_
@api.model
def change_state(self, new_state):
for book in self:
if book.is_allowed_transition(book.state, new_state):
book.state = new_state
else:
msg = _('Moving from %s to %s is not allowed') % (book.state, new_state)
raise UserError(msg)
這邊這段程式碼的用意,主要確認借書狀態是否可以更新,如果不行將回傳Error給使用者,其中_ 在未來翻譯多國語言的時候可以用到。
ValidationError()
-沒有滿足constrains條件報錯AccessError()
-用戶不是群組內的使用者,不得訪問RedirectError()
-報出Error後,指向指定View當中
這些是其他常用報錯的字段
○ Filter record
如果我們想要將特定的record找出來我們可以透過以下方法:
@api.model
def multiple_authors(self,all_book):
def predicate(book):
if len(book.author_ids)>1:
return True
return all_book.filter(predicate)
@api.model
def get_author_names(self,books):
return book.mapped('author_ids.name')
或是排序record:
@api.mode
def sort_book_by_date(self,books):
return books.sorted(key='release_date')
○ sudo()
當我們遇到需要特定全線record時,想將資料呈現給不再此群組中的使用者看,可以使用:
def book_rent(self):
self.ensure_one() #這段在使用sudo()時,非常重要必定要搭配使用
if self.state !='avaliable':
raise UserError(_('book is not available for renting!!'))
rent_as_superuser=self.env['library.book.rent'].sudo()
rent_as_superuser.create({'book_id':self.id,
'borrower_id':self.env.user.partner_id.id,})
另外還有兩種model:
model.AbstractModel
- 通常會跟report一起使用,並且資料不會被儲存或是在進行運算,不想讓DB的負擔太大。model.transientModel
- 通常會用在引導用戶使用,並且是臨時儲存的,在一定的時間內就會被系統刪除舊的數據。