Chương 8: Các Trường Tính Toán Và Onchange¶
The relations between models are a key component of any Odoo module. They are necessary for the modelization of any business case. However, we may want links between the fields within a given model. Sometimes the value of one field is determined from the values of other fields and other times we want to help the user with data entry.
Những trường hợp này được hỗ trợ bởi các khái niệm về trường được tính toán và các thay đổi. Mặc dù chương này không phức tạp về mặt kỹ thuật nhưng ngữ nghĩa của cả hai khái niệm đều rất quan trọng. Đây cũng là lần đầu tiên chúng ta viết logic Python. Cho đến bây giờ chúng ta chưa viết gì khác ngoài định nghĩa lớp và khai báo trường.
Trường tính toán¶
Tham khảo: tài liệu liên quan đến chủ đề này có thể được tìm thấy trong Trường tính toán.
Ghi chú
Mục tiêu: ở cuối phần này:
Trong mô hình bất động sản, tổng diện tích và ưu đãi tốt nhất phải được tính toán:

Trong mô hình cung cấp tài sản, ngày hiệu lực phải được tính toán và có thể cập nhật:

Trong mô-đun bất động sản, chúng tôi đã xác định khu vực sinh hoạt cũng như khu vực sân vườn. Khi đó, điều tự nhiên là định nghĩa tổng diện tích là tổng của cả hai trường. Chúng tôi sẽ sử dụng khái niệm trường được tính toán cho việc này, tức là giá trị của một trường nhất định sẽ được tính từ giá trị của các trường khác.
Cho đến nay các trường đã được lưu trữ trực tiếp và được lấy trực tiếp từ cơ sở dữ liệu. Các trường cũng có thể được tính toán. Trong trường hợp này, giá trị của trường không được truy xuất từ cơ sở dữ liệu mà được tính toán nhanh chóng bằng cách gọi một phương thức của mô hình.
Để tạo trường được tính toán, hãy tạo một trường và đặt thuộc tính của nó compute
thành tên của một phương thức. Phương thức tính toán phải đặt giá trị của trường được tính toán cho mọi bản ghi trong self
.
By convention, compute
methods are private, meaning that they cannot
be called from the presentation tier, only from the business tier (see
Chương 1: Tổng quan về kiến trúc). Private methods have a name starting with an
underscore _
.
phụ thuộc¶
Giá trị của trường được tính toán thường phụ thuộc vào giá trị của các trường khác trong bản ghi được tính toán. ORM yêu cầu nhà phát triển chỉ định các phần phụ thuộc đó vào phương thức tính toán bằng trình trang trí depends()
. ORM sử dụng các phần phụ thuộc đã cho để kích hoạt việc tính toán lại trường bất cứ khi nào một số phần phụ thuộc của nó được sửa đổi:
from odoo import api, fields, models
class TestComputed(models.Model):
_name = "test.computed"
total = fields.Float(compute="_compute_total")
amount = fields.Float()
@api.depends("amount")
def _compute_total(self):
for record in self:
record.total = 2.0 * record.amount
Ghi chú
bản thân
là một bộ sưu tập.
Đối tượng self
là một tập bản ghi, tức là một tập hợp các bản ghi được sắp xếp. Nó hỗ trợ các hoạt động Python tiêu chuẩn trên các bộ sưu tập, ví dụ: len(self)
và iter(self)
, cùng với các thao tác tập hợp bổ sung như `` recs1 | recs2``.
Việc lặp lại self
sẽ cung cấp cho từng bản ghi một, trong đó mỗi bản ghi chính là một tập hợp có kích thước 1. Bạn có thể truy cập/gán các trường trên các bản ghi riêng lẻ bằng cách sử dụng ký hiệu dấu chấm, ví dụ: bản ghi.name
.
Bạn có thể tìm thấy nhiều ví dụ về trường tính toán trong SoOn. Đây là một cách đơn giản.
Exercise
Tính tổng diện tích.
Thêm trường
total_area
vàoestate.property
. Nó được định nghĩa là tổng củakhu_sống_sống
vàkhu_vườn
.Thêm trường trong chế độ xem biểu mẫu như được mô tả trên hình ảnh đầu tiên về Mục tiêu của phần này.
Đối với các trường quan hệ, có thể sử dụng các đường dẫn qua một trường làm phần phụ thuộc:
description = fields.Char(compute="_compute_description")
partner_id = fields.Many2one("res.partner")
@api.depends("partner_id.name")
def _compute_description(self):
for record in self:
record.description = "Test for partner %s" % record.partner_id.name
Ví dụ này được đưa ra với Many2one
, nhưng nó hợp lệ với Many2many
hoặc One2many
. Bạn có thể tìm thấy một ví dụ tại đây.
Hãy thử nó trong mô-đun của chúng tôi với bài tập sau!
Exercise
Tính toán lời đề nghị tốt nhất.
Thêm trường
best_price
vàoestate.property
. Nó được định nghĩa là mức cao nhất (tức là tối đa) củagiá
của ưu đãi.Thêm trường vào chế độ xem biểu mẫu như được mô tả trong hình ảnh đầu tiên về Mục tiêu của phần này.
Mẹo: bạn có thể muốn thử sử dụng phương thức mapped()
. Xem tại đây để biết ví dụ đơn giản.
Chức năng trái ngược¶
Bạn có thể nhận thấy rằng các trường được tính toán ở chế độ chỉ đọc theo mặc định. Điều này được mong đợi vì người dùng không được phép đặt giá trị.
Trong một số trường hợp, việc vẫn có thể đặt giá trị trực tiếp có thể hữu ích. Trong ví dụ về bất động sản của chúng tôi, chúng tôi có thể xác định thời hạn hiệu lực cho một ưu đãi và đặt ngày hiệu lực. Chúng tôi muốn có thể đặt thời lượng hoặc ngày sao cho cái này ảnh hưởng đến cái kia.
Để hỗ trợ điều này, SoOn cung cấp khả năng sử dụng chức năng nghịch đảo
:
from odoo import api, fields, models
class TestComputed(models.Model):
_name = "test.computed"
total = fields.Float(compute="_compute_total", inverse="_inverse_total")
amount = fields.Float()
@api.depends("amount")
def _compute_total(self):
for record in self:
record.total = 2.0 * record.amount
def _inverse_total(self):
for record in self:
record.amount = record.total / 2.0
Bạn có thể tìm thấy một ví dụ tại đây.
Phương thức tính toán đặt trường trong khi phương thức nghịch đảo đặt phần phụ thuộc của trường.
Lưu ý rằng phương thức `` nghịch đảo`` được gọi khi lưu bản ghi, trong khi phương thức `` tính toán`` được gọi mỗi khi thay đổi các phần phụ thuộc của nó.
Exercise
Tính ngày hiệu lực của các phiếu mua hàng.
Thêm các trường sau vào mô hình
estate.property.offer
:
Cánh đồng |
Kiểu |
Mặc định |
---|---|---|
hiệu lực |
số nguyên |
7 |
ngày_hạn chót |
Ngày |
Trong đó date_deadline
là trường được tính toán được xác định là tổng của hai trường từ ưu đãi: ngày tạo
và `` hiệu lực``. Xác định hàm nghịch đảo thích hợp để người dùng có thể đặt ngày hoặc giá trị hợp lệ.
Mẹo: create_date
chỉ được điền khi bản ghi được tạo, do đó bạn sẽ cần một bản dự phòng để tránh bị lỗi tại thời điểm tạo.
Thêm các trường trong chế độ xem biểu mẫu và chế độ xem danh sách như được mô tả trên hình ảnh thứ hai về Mục tiêu của phần này.
thông tin thêm¶
Các trường được tính toán không được lưu trữ trong cơ sở dữ liệu theo mặc định. Vì vậy, không thể tìm kiếm trên trường được tính toán trừ khi phương thức tìm kiếm
được xác định. Chủ đề này nằm ngoài phạm vi của khóa đào tạo này nên chúng tôi sẽ không đề cập đến nó. Bạn có thể tìm thấy một ví dụ tại đây.
Một giải pháp khác là lưu trữ trường với thuộc tính store=True
. Mặc dù điều này thường thuận tiện nhưng hãy chú ý đến tải tính toán tiềm năng được thêm vào mô hình của bạn. Hãy sử dụng lại ví dụ của chúng tôi:
description = fields.Char(compute="_compute_description", store=True)
partner_id = fields.Many2one("res.partner")
@api.depends("partner_id.name")
def _compute_description(self):
for record in self:
record.description = "Test for partner %s" % record.partner_id.name
Mỗi khi tên
đối tác được thay đổi, mô tả
sẽ tự động được tính toán lại cho tất cả các bản ghi đề cập đến nó! Điều này có thể nhanh chóng trở nên khó tính toán lại khi hàng triệu bản ghi cần tính toán lại.
Cũng cần lưu ý rằng một trường được tính toán có thể phụ thuộc vào một trường được tính toán khác. ORM đủ thông minh để tính toán lại chính xác tất cả các phần phụ thuộc theo đúng thứ tự... nhưng đôi khi phải trả giá bằng hiệu suất bị suy giảm.
Nói chung, hiệu suất phải luôn được ghi nhớ khi xác định các trường được tính toán. Trường tính toán của bạn càng phức tạp (ví dụ: có nhiều phụ thuộc hoặc khi trường được tính toán phụ thuộc vào các trường được tính toán khác), thì càng mất nhiều thời gian để tính toán. Luôn dành chút thời gian để đánh giá trước chi phí của trường được tính toán. Hầu hết chỉ khi mã của bạn đến máy chủ sản xuất, bạn mới nhận ra rằng nó làm chậm toàn bộ quá trình. Không mát mẻ :-(
Thay đổi¶
Tham khảo: tài liệu liên quan đến chủ đề này có thể được tìm thấy trong onchange()
:
Ghi chú
Mục tiêu: ở cuối phần này, việc bật khu vườn sẽ đặt diện tích mặc định là 10 và hướng về phía Bắc.

Trong mô-đun bất động sản, chúng tôi cũng muốn trợ giúp người dùng nhập dữ liệu. Khi trường 'khu vườn' được đặt, chúng tôi muốn đưa ra giá trị mặc định cho diện tích khu vườn cũng như hướng. Ngoài ra, khi trường 'khu vườn' không được đặt, chúng tôi muốn khu vườn được đặt lại về 0 và xóa hướng. Trong trường hợp này, giá trị của một trường nhất định sẽ sửa đổi giá trị của các trường khác.
Cơ chế 'onchange' cung cấp một cách để giao diện máy khách cập nhật biểu mẫu mà không lưu bất kỳ thứ gì vào cơ sở dữ liệu bất cứ khi nào người dùng điền vào một giá trị trường. Để đạt được điều này, chúng tôi xác định một phương thức trong đó self
đại diện cho bản ghi trong chế độ xem biểu mẫu và trang trí nó bằng onchange()
để chỉ định trường nào được kích hoạt. Mọi thay đổi bạn thực hiện trên self
sẽ được phản ánh trên biểu mẫu:
from odoo import api, fields, models
class TestOnchange(models.Model):
_name = "test.onchange"
name = fields.Char(string="Name")
description = fields.Char(string="Description")
partner_id = fields.Many2one("res.partner", string="Partner")
@api.onchange("partner_id")
def _onchange_partner_id(self):
self.name = "Document for %s" % (self.partner_id.name)
self.description = "Default description for %s" % (self.partner_id.name)
Trong ví dụ này, việc thay đổi đối tác cũng sẽ thay đổi tên và giá trị mô tả. Người dùng có quyền thay đổi tên và giá trị mô tả sau đó hay không. Cũng lưu ý rằng chúng tôi không lặp lại self
, điều này là do phương thức này chỉ được kích hoạt trong chế độ xem biểu mẫu, trong đó self
luôn là một bản ghi duy nhất.
Exercise
Đặt giá trị cho khu vườn và hướng.
Tạo một onchange
trong mô hình estate.property
để đặt giá trị cho diện tích vườn (10) và hướng (hướng Bắc) khi vườn được đặt thành True. Khi không được đặt, hãy xóa các trường.
thông tin thêm¶
Các phương thức Onchanges cũng có thể trả về thông báo cảnh báo không chặn (ví dụ).
Làm thế nào để sử dụng chúng?¶
Không có quy tắc nghiêm ngặt nào cho việc sử dụng các trường được tính toán và các thay đổi.
Trong nhiều trường hợp, cả trường được tính toán và các thay đổi có thể được sử dụng để đạt được kết quả tương tự. Luôn ưu tiên các trường được tính toán vì chúng cũng được kích hoạt bên ngoài ngữ cảnh của chế độ xem biểu mẫu. Đừng bao giờ sử dụng onchange để thêm logic nghiệp vụ vào mô hình của bạn. Đây là một ý tưởng rất tệ vì các thay đổi không được kích hoạt tự động khi tạo bản ghi theo chương trình; chúng chỉ được kích hoạt trong chế độ xem biểu mẫu.
Cạm bẫy thông thường của các trường tính toán và các thay đổi đang cố gắng trở nên 'quá thông minh' bằng cách thêm quá nhiều logic. Điều này có thể dẫn đến kết quả ngược lại với những gì được mong đợi: người dùng cuối bối rối trước tất cả quá trình tự động hóa.
Các trường được tính toán có xu hướng dễ gỡ lỗi hơn: trường như vậy được đặt theo một phương thức nhất định, do đó rất dễ theo dõi khi giá trị được đặt. Mặt khác, các thay đổi có thể gây nhầm lẫn: rất khó để biết mức độ của một thay đổi. Vì một số phương thức trao đổi có thể đặt các trường giống nhau nên việc theo dõi giá trị đến từ đâu sẽ dễ dàng trở nên khó khăn.
Khi sử dụng các trường tính toán được lưu trữ, hãy chú ý đến các phần phụ thuộc. Khi các trường được tính toán phụ thuộc vào các trường được tính toán khác, việc thay đổi một giá trị có thể kích hoạt một số lượng lớn các lần tính toán lại. Điều này dẫn đến hiệu suất kém.
In the next chapter, we'll see how we can trigger some business logic when buttons are clicked.