老韓頭的開發日常 ☞ 【好書學習】系列
通過odoo框架,我們可以開發大型且復雜的應用。良好的性能是實現這一目標的基礎。本章,我們將探討如何提高應用性能。同時,我們也會講解找出影響性能的因素。
本章包含以下內容:
- 記錄集的預讀取模式
- 將數據在內存中緩存
- 生成不同尺寸的圖片
- 訪問組數據
- 一次性創建或寫多條數據
- 通過數據庫查詢訪問數據
- 優化python代碼
記錄集的預讀取模式
當我們訪問數據時,內部其實是執行了SQL查詢。如果我們在一個多條數據的數據集讀取數據時,由於內部執行了多條SQL語句,這可能導致系統響應會比較慢。本節,我們將探討如何通過預讀取的方式優化效率。通過如下預讀取模式,我們可以減少SQL查詢的數量,進而優化系統性能。
步驟
下面的代碼是計算函數。在這個方法中,self是包含多條數據的數據集。當我們直接在數據集進行迭代查詢的時候,預取可以完美地工作:
# Correct prefetching
def compute_method(self):
for rec in self:
print(rec.name)
但是在某些場景下,預加載將變得十分復雜。比如,當通過browse方法獲取數據的時候。在下面的例子中,我們通過for循環一個個的獲取數據。這時將執行多次的SQL查詢,效率就比較低了:
# Incorrect prefetching
def some_action(self):
record_ids = []
self.env.cr.execute("some query to fetch record id")
for rec in self.env.cr.fetchall():
record = self.env['res.partner'].browse(rec[0])
print(record.name)
通過將ID的列表傳給browse方法,我們可以創建一個包含多條數據的數據集。如下代碼,可通過預加載模式實現優化:
# Correct prefetching
def some_action(self):
record_ids = []
self.env.cr.execute("some query to fetch record id")
record_ids = [rec[0] for rec in self.env.cr.fetchall()]
recordset = self.env["res.partner"].browse(record_ids)
for record_id in recordset:
print(record.name)
這種方式,將在一個SQL查詢的情況下實現預加載。
原理
當我們操作多數據集的時候,通過預加載可以減少SQL查詢的數量。它可以通過一次性獲取所有的數據。通常,預加載是odoo內部自動實現的,但是在某些場景下將失去此特性。比如,我們像如下方式分割數據:
recs = [r for r in recordset r.id not in [1,2,4,10]]
由於我們將數據集拆分成幾部分,因此odoo內部將無法實現一次性預加載。
通過正確的預加載可以大幅提高對象-關系映射(Object-Relational Mapping,ORM)的性能。當我們通過for循環迭代數據集的時候,在第一次迭代訪問字段值的時候,預加載將發揮其魔力。預加載將加載數據集所有的數據。后續,我們再迭代的時候將直接通過從緩存讀取。這可以將SQL查詢的復雜的由O(n)降至O(1)。
我們假設數據集有10條數據。當我們在第一次循環中獲取name字段的值,他將獲取10條數據。並不只是name字段,而是10條數據的所有字段。后續迭代的數據將直接從緩存獲取。復雜度將由10降至1。
for record in recordset: # recordset with 10 records
record.name # Prefetch data of all 10 records in the first
loop
record.email # data of email will be served from the cache.
預加載可以獲取除*2many字段以外的字段的值。即便某些字段在循環體內並未使用。因為加載多余的字段所帶來的性能影響遠小於額外的查詢。
小貼士
有時,預加載會降低性能。這時,我們可以通過recordset.with_context(prefetch_fields=Flase)禁用預加載。
預加載機制使用的是環境內存存儲和檢索記錄值。這就意味着,一旦數據從數據庫檢索出來,后續所有的數據都將從緩存查詢。我們可以通過env.cache獲取環境緩存。我們可以使用invalidate_cache()函數禁用緩存。
更多
如果我們分隔了數據集,ORM將重新生成帶有新的預加載上下文的數據集。這時,這些數據集將僅加載其自身代表的數據的內容。如果我們打算在預加載后加載所有的數據,我們可以使用with_pretetch()函數。在下面的例子中,我們將數據集分割為兩部分。我們在兩個記錄集中都傳遞了一個通用的預取上下文,所以當你從其中一個記錄中獲取數據時,ORM會為另一個獲取數據並將數據放入緩存中以備將來使用:
recordset = ... # assume recordset has 10 records.
recordset1 = recordset[:5].with_prefetch(recordset._ids)
recordset2 = recordset[5:].with_prefetch(recordset._ids)
預取上下文不限於拆分記錄集。您也可以使用with_ prefetch()方法在多個記錄集之間擁有一個公共的預取上下文。這意味着當您從一條記錄中獲取數據時,它也會為所有其他記錄集獲取數據。
將數據在內存中緩存
odoo框架提供了ormcache裝飾器管理內存緩存。本節,我們將探討如何管理緩存。
步驟
ORM緩存類定義在/odoo/tools/cache.py中。
引入文件:
from odoo import tools
ormcache
這是最常用的緩存裝飾器。您需要傳遞方法輸出所依賴的參數名。下面是一個帶有ormcache裝飾器的示例方法:
@tools.ormcache('mode')
def fetch_mode_data(self, mode):
# some calculations
return result
當我們首次調用該函數的時候,將會返回計算值。ormcache將會存儲mode的值及result的值。如果我們再次調用該函數,且mode的值為之前存在的值時,將直接返回result的值。
有時,我們的函數依賴於環境屬性。比如:
@tools.ormcache('self.env.uid', 'mode')
def fetch_data(self, mode):
# some calculations
return result
該函數將根據當前用戶及mode的值存儲result的值。
ormcache_context
該裝飾器與ormcache類似,不同的是它依賴於參數和上下文中的值。我們需要傳入參數名稱及上線文鍵的列表。例如,我們的輸出依賴於上下文的lang及website_id,如下:
@tools.ormcache_context('mode', keys=('website_id', 'lang'))
def fetch_data(self, mode):
# some calculations
return result
該緩存將依賴於mode及context中的值
ormcache_multi
有些方法對多個記錄或id執行操作。如果你想在這些方法上添加緩存,你可以使用ormcache_multi裝飾器。您需要傳遞multi參數,在方法調用期間,ORM將通過迭代該參數生成緩存鍵。在這個方法中,您將需要以字典格式返回結果,並以multi參數的元素作為鍵。看看下面的例子:
@tools.ormcache_multi('mode', multi='ids')
def fetch_data(self, mode, ids):
result = {}
for i in ids:
data = ... # some calculation based on ids
result[i] = data
return result
假設我們用[1,2,3]作為id調用前面的方法。該方法將返回一個結果{1:…2:…3:…}格式。ORM將基於這些鍵緩存結果。如果你用[1,2,3,4,5]作為ID進行另一個調用,你的方法將接收[4,5]作為ID參數,所以方法將執行4和5個ID的操作,其余的結果將從緩存中提供。
原理
ORM緩存以字典的形式保存緩存(緩存查找)。該緩存的鍵將基於裝飾方法的簽名生成,結果將是值。簡單地說,當您使用x, y參數調用方法時,方法的結果是x+y,緩存查找將是{(x, y): x+y}。這意味着下次使用相同的參數調用該方法時,結果將直接從該緩存中提供。這節省了計算時間,使響應更快。
ORM緩存是一個內存緩存,所以它被存儲在RAM中並占用內存。不要使用ormcache來提供大型數據,例如圖像或文件。
警告
使用此裝飾器的方法永遠不應返回記錄集。如果您這樣做,它們將生成psycopg2.OperationalError,因為記錄集的基礎游標已關閉。
你應該在純函數上使用ORM緩存。純函數是指對於相同的參數總是返回相同結果的方法。這些方法的輸出僅取決於參數,因此它們返回相同的結果。如果不是這種情況,則需要在執行使緩存狀態無效的操作時手動清除緩存。要清除緩存,調用clear_cache()方法:
self.env[model_name].clear_caches()
一旦清除了緩存,下一個對方法的調用將執行該方法並將結果存儲在緩存中,所有具有相同參數的后續方法調用都將從緩存中提供服務。
更多
ORM緩存是Least Recently
Used(LRU),這意味着如果一個鍵在緩存中不經常使用,它將被刪除。如果你沒有正確地使用ORM緩存,它可能弊大於利。例如,如果在方法中參數總是不同的,那么每次Odoo都會先在緩存中查找,然后調用方法來計算。如果你想了解你的緩存是如何執行的,你可以把SIGUSR1信號傳遞給Odoo進程:
kill -SIGUSER1 496
其中,496為進程號。執行命令后,可以在日志中看到ORM緩存的狀態。

緩存中的百分比是命中率。它是在緩存中找到結果的成功率。如果緩存的命中率太低,你應該從方法中刪除ORM緩存。
生成不同尺寸的圖片
大圖片對任何網站來說都是麻煩的。它們增加了網頁的大小,結果使網頁變慢。這就導致了不好的SEO排名和訪問者流失。本節,我們將探索如何創建不同大小的圖像;通過使用正確的圖像,您可以減少網頁大小和改善頁面加載時間。
步驟
您將需要繼承image.mixin。如下:
class LibraryBook(models.Model):
_name = 'library.book'
_description = 'Library Book'
_inherit = ['image.mixin']
mixin模型將自動添加5個字段,用於存儲不同大小的圖片。
步驟
image.mixin實例將自動向模型添加5個新的二進制字段。每個字段存儲具有不同分辨率的圖像。以下是這些領域及其解決方案的列表:
- image_1920: 1,920x1,920
- image_1024: 1,024x1,024
- image_512: 512x1,512
- image_256: 256x256
- image_128: 128x128
在這里給出的所有字段中,只有image_1920是可編輯的。其他圖像字段是只讀的,當您更改image_1920字段時,它們會自動更新。因此,在模型的后端表單視圖中,您需要使用image_1920字段來允許用戶上傳圖像。但這樣做,我們在表單視圖中加載大image_1920圖像。但是,有一種方法可以提高性能,即在form視圖中使用image_1920圖像,但是顯示較小的圖像。例如,我們可以使用image_1920字段,但顯示image_128字段。要做到這一點,你可以使用以下語法:
<field name="image_1920" widget="image" options="{'preview_image': 'image_128'}" />
將圖像保存到字段后,Odoo會自動調整圖像的大小並將其存儲到相應的字段中。form視圖將顯示轉換后的image_128,因為我們使用它作為preview_image。
小貼士
image.mixin模型是AbstractModel,所以它的表不在數據庫中。為了使用它,您需要在模型中繼承它。
image.mixin,您可以存儲的圖像的最大分辨率為1920 × 1920。如果您保存的圖像分辨率高於1920 x1920, Odoo會將其降低為1920 x1920。在這樣做的同時,Odoo還將保留圖像的分辨率,避免任何失真。例如,如果您上傳的圖像分辨率為2,400x1,600,那么image_1920字段的分辨率將為1,920x1,280。
更多
image.mixin,你可以獲得特定分辨率的圖像,但是如果你想使用另一種分辨率的圖像呢?為此,您可以使用二進制包裝字段圖像,如下面的示例所示:
image_1500 = fields.Image("Image 1500", max_width=1500, max_ height=1500)
這將創建一個新的image_1500字段,存儲圖像將把它的分辨率調整為1500 x1500。注意,這不是image.mixin的一部分。它只是將圖像縮小為1,500x1,500,因此需要在form視圖中添加該字段;編輯它不會改變image.mixin中的其他圖像字段。如果您想將其與現有的圖像鏈接。在字段定義中添加related="image_1920"屬性。
訪問組數據
當需要用於統計的數據時,通常需要以分組的形式提供數據,例如月度銷售報告,或者顯示每個客戶銷售額的報告。手動搜索記錄並將它們分組是很耗時的。在這篇文章中,我們將探討如何使用read_group()方法訪問分組數據。
步驟
小貼士
read_group()方法廣泛用於統計和智能統計按鈕。
- 讓我們假設您想要在合作伙伴表單上顯示銷售訂單的數量。這可以通過搜索客戶的銷售訂單,然后計算長度來實現:
# in res.partner model
so_count = fields.Integer(compute='_compute_so_count', string='Sale order count')
def _compute_so_count(self):
sale_orders = self.env['sale.order'].search(domain=[('partner_id', 'in', self.ids)])
for partner in self:
partner.so_count = len(sale_orders.filtered(lambda so: so.partner_id.id == partner.id))
前面的示例可以工作,但不是最優的。當您在樹視圖上顯示so_count字段時,它將獲取並過濾列表中所有合作伙伴的銷售訂單。對於這樣少量的數據,read_group()方法不會產生太大的影響,但隨着數據量的增長,它可能會成為一個問題。要修復這個問題,可以使用read_ group方法。
- 下面的示例與前面的示例相同,但它只使用一個SQL查詢,即使是大型數據集:
# in res.partner model
so_count = fields.Integer(compute='_compute_so_count', string='Sale order count')
def _compute_so_count(self):
sale_data = self.env['sale.order'].read_group(domain=[('partner_id', 'in', self.ids)],fields=['partner_id'], groupby=['partner_id'])
mapped_data = dict([(m['partner_id'][0], m['partner_id_count']) for m in sale_data])
for partner in self:
partner.so_count = mapped_data[partner.id]
前面的代碼片段是優化的,因為它直接通過SQL的group BYfeature獲取銷售訂單計數。
原理
read_group()方法在內部使用SQL的GROUP BY特性。這使得read_group方法更快,即使你有大的數據集。在內部,Odoo web客戶端在圖表和分組樹視圖中使用這種方法。您可以通過使用不同的參數來調整read_group方法的行為。
讓我們來研究read_group方法:
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
read_group方法可用的不同參數如下:
- domain: 這用於過濾記錄。這將是read_group方法的搜索條件。
- fields: 這是要在分組中獲取的字段列表。注意,這里提到的字段應該在groupby參數中,除非您使用一些聚合函數。read_group方法支持SQL聚合函數。假設您想要得到每個客戶的平均訂單量。在這種情況下,可以使用read_group,如下所示:
self.env['sale.order'].read_group([], ['partner_id', 'amount_total:avg'], ['partner_id'])
如果您想兩次訪問同一個字段,但使用不同的聚合函數,則語法略有不同。您需要將字段名作為別alias:agg(field_ name)。這個例子會給你每個客戶的總訂單數和平均訂單數:
self.env['sale.order'].read_group([], ['partner_id',
'total:sum(amount_total)', 'avg_total:avg(amount_ total)'], ['partner_id'])
- groupby: 該參數將是記錄分組的字段列表。它允許您基於多個字段對記錄進行分組。為此,您需要傳遞一個字段列表。例如,如果您想按客戶和訂單狀態對銷售訂單進行分組,可以在此參數中傳遞['partner_id ', 'state']。
- offset: 此參數用於分頁。如果要跳過幾條記錄,可以使用此參數。
- limit: 此參數用於分頁;它表示可以獲取的最大記錄數。
- lazy: 這個參數接受布爾值。缺省值為True。如果該參數為真,則只根據groupby參數中的第一個字段對結果進行分組。您將在__context中獲得剩余的groupby參數和域,並在結果中獲得__domain鍵。如果該參數的值設置為False,它將根據groupby參數中的所有字段對數據進行分組。
更多
根據日期字段進行分組可能比較復雜,因為可以根據天、周、季度、月或年對記錄進行分組。您可以通過在groupby參數的:后面傳遞groupby_function來更改date字段的分組行為。如果你想把每月的銷售訂單總數分組,你可以使用read_group方法:
日期分組的可能選項有日、周、月、季和年。
參考
如果您想了解更多關於PostgreSQL聚合函數的信息,請參考文檔:https://www.postgresql.org/docs/current/functions-aggregate.html 。
一次性創建或寫多條數據
如果您是Odoo開發新手,您可能會執行多個查詢來編寫或創建多個記錄。本節,我們將了解如何批量創建和寫入記錄。
步驟
創建多個記錄並在多個記錄上寫入數據對於每個記錄的工作原理是不同的。讓我們逐個看一下這些記錄。
創建多數據
Odoo支持批量創建記錄。如果要創建單個記錄,只需傳遞一個包含字段值的字典。要在批處理中創建記錄,您只需要傳遞這些字典的列表,而不是單個字典。下面的示例在一個create調用中創建三條圖書記錄:
vals = [
{
"name": "Book1",
"date_release": "2018/12/12",
},
{
"name": "Book2",
"date_release": "2018/12/12",
},
{
"name": "Book3",
"date_release": "2018/12/12",
},
]
self.env["library.book"].create(vals)
寫多條數據
如果您正在處理Odoo的多個版本,那么您應該意識到write方法在底層是如何工作的。在版本13中,Odoo寫方式有所不同。它使用一種延遲的更新方法,這意味着它不會立即將數據寫入數據庫。Odoo只在必要時或調用flush()時才將數據寫入數據庫。
如下:
# Example 1
data = {...}
for record in recordset:
record.write(data)
# Example 2
data = {...}
recordset.write(data)
如果您使用的是Odoo v13或以上版本,那么就不會有任何性能方面的問題。但是,如果您使用的是較舊的版本,則第二個示例將比第一個示例快得多,因為第一個示例將在每次迭代中執行一個SQL查詢。
原理
為了在批處理中創建多個記錄,您需要以列表的形式傳遞值字典來創建新記錄。這將自動管理批量創建記錄。當您在批處理中創建記錄時,在內部這樣做將為每個記錄插入一個查詢。這意味着在批處理中創建記錄不是在單個查詢中完成的。然而,這並不意味着批量創建記錄不能提高性能。通過批量計算獲得性能增益。
write方法的工作方式有所不同。大多數事情都由框架自動處理。例如,如果在所有記錄上寫入相同的數據,則只需要一個UPDATE查詢就可以更新數據庫。如果你在同一個事務中一次又一次地更新相同的記錄,框架甚至會處理它,如下所示:
recordset.name= 'Admin'
recordset.email= 'admin@example.com'
recordset.name= 'Administrator'
recordset.email= 'admin-2@example.com'
在前面的代碼片段中,對於write只執行一個查詢,其最終值為name=Administrator和email=admin-2@example.com。這不會對性能造成不好的影響,因為分配的值在緩存中,稍后會在單個查詢中寫入。
如果在兩者之間使用flush()方法,情況就不一樣了,如下面的示例所示:
recordset.name= 'Admin'
recordset.email= 'admin@example.com'
recordset.flush()
recordset.name= 'Administrator'
recordset.email= 'admin-2@example.com'
flush()方法的作用是:將緩存中的值更新到數據庫。因此,在前面的示例中,將執行兩個更新查詢;一個查詢在刷新之前有數據,第二個查詢在刷新之后有數據。
更多
延遲的更新僅適用於Odoo版本13,如果您使用的是較舊的版本,那么寫入單個值將立即執行更新查詢。檢查以下示例來探索老版本Odoo的寫操作的正確用法:
# incorrect usage
recordset.name= 'Admin' recordset.email= 'admin@example.com'
# correct usage
recordset.write({'name': 'Admin', 'email'= 'admin@example. com'})
在第一個示例中,我們有兩個UPDATE查詢,而第二個示例只有一個更新查詢。
通過數據庫查詢訪問數據
Odoo ORM的方法有限,有時很難從ORM中獲取某些數據。在這種情況下,您可以按照需要的格式獲取數據,並且需要對數據進行操作才能得到特定的結果。因此,它會變慢。為了處理這些特殊情況,您可以在數據庫中執行SQL查詢。在這個食譜中,我們將探索如何從Odoo運行SQL查詢。
步驟
使用self._cr.execute方法
- 添加代碼
self.flush()
self._cr.execute("SELECT id, name, date_release FROM library_book WHERE name ilike %s", ('%odoo%',))
data = self._cr.fetchall()
print(data)
Output:
[(7, 'Odoo basics', datetime.date(2018, 2, 15)), (8, 'Odoo 11 Development Cookbook', datetime.date(2018, 2, 15)), (1, 'Odoo 12 Development Cookbook', datetime. date(2019, 2, 13))]
- 查詢的結果將以元組列表的形式出現。元組中的數據與查詢中的字段的順序相同。如果你想獲取字典格式的數據,你可以使用dictfetchall()方法。看看下面的例子:
self.flush()
self._cr.execute("SELECT id, name, date_release FROM library_book WHERE name ilike %s", ('%odoo%',))
data = self._cr.dictfetchall()
print(data)
Output:
[{'id': 7, 'name': 'Odoo basics', 'date_release': datetime.date(2018, 2, 15)}, {'id': 8, 'name': 'Odoo 11 Development Cookbook', 'date_release': datetime. date(2018, 2, 15)}, {'id': 1, 'name': 'Odoo 12 Development Cookbook', 'date_release': datetime. date(2019, 2, 13)}]
如果你只想獲取一條記錄,你可以使用fetchone()和dictfetchone()方法。這些方法的工作原理類似於fetchall()和dictfetchall(),但它們只返回一條記錄,如果您想獲取多條記錄,則需要多次調用fetchone()和dictfetchone()方法。
原理
有兩種方法可以從記錄集訪問數據庫游標:一種是從記錄集本身,如self._cr,另一個來自環境,特別是self.env.cr。此游標用於執行數據庫查詢。在前面的示例中,我們看到了如何通過原始查詢獲取數據。表名是替換后的型號名稱。用_表示庫。圖書模型變成了library_book。
如果你注意到了,我們在執行查詢之前使用了self.flush()。這背后的原因是Odoo過度使用緩存,數據庫可能沒有正確的值。self.flush()會將所有延遲的更新推送到數據庫,並執行所有相關的計算,這樣你就會從數據庫中獲得正確的值。flush()方法還支持一些參數,這些參數可以幫助您控制數據庫中正在刷新的內容。參數說明如下:
- fname: 要刷新到數據庫的字段列表。
- records: 要刷新到數據庫的數據集。
如果您正在執行INSERT或UPDATE查詢,您還需要在執行查詢之后執行flush(),因為ORM可能不知道您所做的更改,而且它可能已經緩存了記錄。
在執行原始查詢之前,需要考慮一些事情。只有在別無選擇時才使用原始查詢。通過執行原始查詢,您繞過了ORM層。因此,您也繞過了安全規則和ORM的性能優勢。有時,錯誤構建的查詢可能會引入SQL注入漏洞。考慮以下示例,其中查詢可能允許攻擊者執行SQL注入:
# very bad, SQL injection possible
self.env.cr.execute('SELECT id, name FROM library_book WHERE name ilike + search_keyword + ';')
# good
self.env.cr.execute('SELECT id, name FROM library_book WHERE name ilike %s ';', (search_keyword,))
也不要使用字符串格式函數;它還允許攻擊者執行SQL注入。使用SQL查詢會使其他開發人員難以閱讀和理解您的代碼,所以盡可能避免使用它們。
信息
許多Odoo開發人員認為,執行SQL查詢可以使操作更快,因為它繞過了ORM層。然而,這並不完全正確;這要看具體情況。在大多數操作中,ORM比原始查詢執行得更好更快,因為數據是從記錄集緩存中提供的。
更多
在一個事務中完成的操作只在事務結束時提交。如果ORM中發生錯誤,事務將回滾。如果你已經做了一個插入或更新查詢,並且你想讓它永久存在,你可以使用self._cr.commit()來提交更改。
小貼士
注意,使用commit()可能是危險的,因為它可能將記錄置於不一致的狀態。ORM中的錯誤可能會導致不完全回滾,所以只有在你完全確定自己在做什么時才使用commit()。
如果使用了commit()方法,那么之后就不需要使用flush()。commit()方法在內部刷新環境。
優化python代碼
有時,你將無法查明問題的原因。在性能問題上尤其如此。Odoo提供了一些內置的分析工具,可以幫助您找到問題的真正原因。
步驟
- odoo的分析器可以在Odoo /tools/profiler.py上找到。為了在你的代碼中使用profiler,請將它導入文件:
from odoo.tools.profiler import profiler
- 導入之后,您可以在這些方法上使用概要文件裝飾器。要對一個特定的方法進行概要分析,您需要向它添加概要分析裝飾器。看一下下面的例子。我們把概要文件裝飾器放在make_available方法中:
@profile
def make_available(self):
if self.state != 'lost':
self.write({'state': 'available'})
return True
- 當這個方法被調用時,它將在日志中打印完整的統計信息:

原理
在方法上添加profile裝飾器之后,當您調用該方法時,Odoo將在日志中打印完整的統計信息,如前面的示例所示。它將以三列的形式打印統計數據。第一列將包含調用的次數或執行一行的次數。(當該行處於for循環中或方法是遞歸的時候,這個數字會增加。)第二列表示用給定行觸發的查詢的數量。最后一列是給定行所花費的時間,以毫秒為單位。注意,此列中顯示的時間是相對的;當分析器關閉時,它會更快。
分析器裝飾器接受一些可選參數,這些參數可以幫助您獲得該方法的詳細統計信息。下面是profiler裝飾器的定義:
def profile(method=None, whitelist=None, blacklist=(None,), files=None, minimum_time=0, minimum_queries=0):
下面是profile()方法支持的參數列表:
- whiltelist: 在日志中顯示的模型名稱列表。
- files: 要顯示的文件名列表。
- blacklist: 不希望在日志中顯示的模型名稱列表。
- minimum_time: 這將接受一個整數值(以毫秒為單位)。它將隱藏總時間小於給定時間的日志。
- minimum_queries: 這將接受查詢數的整數值。它將隱藏查詢總數小於給定數量的日志。
更多
Odoo中還有一種類型的分析器可以為執行的方法生成圖表。這個分析器可以在misc包中找到,所以您需要從那里導入它。它將生成一個帶有統計數據的文件,該數據將生成一個圖形文件。要使用這個分析器,您需要將文件路徑作為參數傳遞。當這個函數被調用時,它將在給定的位置生成一個文件。看一下下面的示例,它生成make_available.prof文件:
from odoo.tools.misc import profile
...
@profile('/Users/parth/Desktop/make_available.profile')
def make_available(self):
if self.state != 'lost':
self.write({'state': 'available'})
self.env['res.partner'].create({'name': 'test', 'email': 'test@ada.asd'})
return True
當調用make_available方法時,它將在桌面上生成一個文件。要將此數據轉換為圖形數據,您需要安裝gprof2dot工具,然后執行以下命令生成圖形:
gprof2dot -f pstats -o /Users/parth/Desktop/prof.xdot /Users/ parth/Desktop/make_available.profile
這個命令將在桌面上生成prof.xdot文件。然后,您可以使用以下命令用xdot顯示圖形:
xdot /Users/parth/Desktop/prof.xdot
使用上述xdot命令將生成如下圖所示的圖形:

