Phát triển phía máy chủ cơ bản Trong chương này, chúng tôi sẽ đề cập đến các chủ đề sau:
1
MỤC LỤC Phát triển phía máy chủ cơ bản ..................................................................................1 1. Xác định các phương thức mô hình và sử dụng các trình trang trí API ..............5 Sẵn sàng .....................................................................................................................6 Làm thế nào để làm nó… ...........................................................................................7 Làm thế nào nó hoạt động… ......................................................................................9 Ẩn các phương thức từ giao diện RPC ....................................................................12 Trình trang trí @ api.one..........................................................................................12 Xem thêm .................................................................................................................13 2. Báo cáo lỗi cho người dùng ...............................................................................13 Sẵn sàng ...................................................................................................................14 Làm thế nào nó hoạt động… ....................................................................................16 Còn nữa… ................................................................................................................17 3. Lấy một recordset rỗng cho một mô hình khác .................................................18 Sẵn sàng ...................................................................................................................18 Làm nó thế nào….....................................................................................................19 Làm thế nào nó hoạt động… ....................................................................................20 Xem thêm .................................................................................................................20 4. Tạo bản ghi mới .................................................................................................21 Sẵn sàng ...............................................................................................................21 Làm thế nào để làm nó… .....................................................................................23 Làm thế nào nó hoạt động…................................................................................25 Còn nữa ................................................................................................................27 5. Cập nhật giá trị của bản ghi recordset ...............................................................28 Sẵn sàng ...............................................................................................................28 Làm thế nào để làm nó… .....................................................................................29 2
Làm thế nào nó hoạt động…................................................................................29 Còn nữa… ............................................................................................................32 6. Tìm kiếm bản ghi ...............................................................................................34 Sẵn sàng ...............................................................................................................34 Làm thế nào để làm nó… .....................................................................................35 Làm thế nào nó hoạt động…................................................................................36 Còn nữa… ............................................................................................................38 7. Kết hợp recordsets .............................................................................................39 Sẵn sàng ...............................................................................................................39 Làm thế nào để làm nó… .....................................................................................40 Làm thế nào nó hoạt động…................................................................................41 Còn nữa… ............................................................................................................42 8. Lọc các tập bản ghi ............................................................................................43 Sẵn sàng ...............................................................................................................44 Làm thế nào để làm nó… .....................................................................................44 Làm thế nào nó hoạt động…................................................................................45 Còn nữa… ............................................................................................................46 9. Traversing bản ghi quan hệ ...............................................................................47 Sẵn sàng ...............................................................................................................47 Làm thế nào để làm nó… .....................................................................................48 Làm thế nào nó hoạt động…................................................................................49 Còn nữa… ............................................................................................................50 Xem thêm .............................................................................................................51 10.
Mở rộng logic nghiệp vụ được định nghĩa trong một Mô hình .....................52
Sẵn sàng ...............................................................................................................52 Làm thế nào để làm nó… .....................................................................................54 Làm thế nào nó hoạt động…................................................................................56 3
Còn nữa… ............................................................................................................57 11.
Mở rộng write () và tạo () ...............................................................................59
Sẵn sàng ...............................................................................................................60 Làm thế nào để làm nó… .....................................................................................61 Làm thế nào nó hoạt động…................................................................................64 Còn nữa… ............................................................................................................66 12.
Tùy chỉnh cách tìm kiếm hồ sơ ......................................................................67
Sẵn sàng ...............................................................................................................68 Làm thế nào để làm nó… .....................................................................................69 Làm thế nào nó hoạt động…................................................................................71 Còn nữa… ............................................................................................................73 Xem thêm .............................................................................................................74
4
Giới thiệu Trong Chương 4, Mô hình ứng dụng, chúng ta đã thấy cách khai báo hoặc mở rộng mô hình kinh doanh trong các mô-đun tùy chỉnh. Các công thức trong chương này bao gồm các phương thức viết cho các trường được tính toán cũng như các phương thức để hạn chế các giá trị của các trường. Chương này tập trung vào các khái niệm cơ bản về phát triển phía máy chủ trong Odoo — các định nghĩa phương thức, thao tác bản ghi và mở rộng các phương thức kế thừa.
1. Xác định các phương thức mô hình và sử dụng các trình trang trí API Các lớp mô hình xác định các mô hình dữ liệu tùy chỉnh khai báo các trường cho dữ liệu được mô hình xử lý. Họ cũng có thể xác định hành vi tùy chỉnh bằng cách xác định các phương thức trên lớp mô hình. 5
Trong công thức này, chúng ta sẽ xem cách viết một phương thức có thể được gọi bằng một nút trong giao diện người dùng, hoặc bởi một số đoạn mã khác trong ứng dụng của chúng ta. Phương thức này sẽ hành động trên LibraryBooks và thực hiện các hành động cần thiết để thay đổi trạng thái của một tuyển tập sách.
Sẵn sàng Công thức này giả sử bạn có một cá thể sẵn sàng, với mô đun addon my_module sẵn có, như được mô tả trong Chương 3, Tạo các Mô-đun Odoo. Bạn sẽ cần phải thêm một trường trạng thái vào mô hình LibraryBook được định nghĩa như sau:
Tạo trạng thại from openerp import models, fields, api class LibraryBook (models.Model): # […] state = fields.Selection ([('draft', 'Unavailable'), ('có sẵn có sẵn'), ('mượn', 'Vay'), ('mất', 'Bị mất')], 'Tiểu bang')
6
Vui lòng tham khảo Chương 4, Thêm các trường dữ liệu vào một Mô hình để rõ ràng hơn.
Làm thế nào để làm nó… Để xác định một phương thức trên LibraryBook để cho phép thay đổi trạng thái của một tuyển tập sách, bạn cần phải thêm đoạn mã sau vào định nghĩa mô hình: 1. Thêm một phương thức trợ giúp để kiểm tra xem có cho phép chuyển tiếp trạng thái hay không:
Cho phép chuyén tiép trạng thại (ĐK chuyén tiép trạng thại)
@ api.model def is_allowed_transition (self, old_state, new_state): allowed = [('draft', 'available'), ('có sẵn', 'mượn'), ('mượn', 'có sẵn'), ('có sẵn', 'bị mất'), ('mượn', 'mất'), ('bị mất', 'có sẵn')] return (old_state, new_state) được cho phép 7
2. Thêm một phương thức để thay đổi trạng thái của một số sách sang một cái mới được chuyển thành một đối số:
Đoi trạng thại
@ api.multi def change_state (tự, new_state): for book in self: nếu book.is_allowed_transition (book.state, new_state): book.state = new_state khác: tiếp tục
8
Làm thế nào nó hoạt động… Mã trong công thức định nghĩa hai phương thức. Chúng là các phương thức Python bình thường, có self là đối số đầu tiên của nó và cũng có thể có đối số bổ sung. Các phương thức được trang trí với các trang trí từ mô-đun openerp.api. Những 9
trang trí có sẵn để đảm bảo việc chuyển đổi cuộc gọi được thực hiện bằng API cũ hoặc API truyền thống API mới. API cũ được sử dụng bởi giao thức gọi thủ tục từ xa (RPC) của trình khách web và theo các mô-đun chưa được chuyển sang API mới. Phần quan trọng của chuyển đổi này là việc tạo ra một môi trường thực thi được lưu trữ trong self.env; nó bao gồm: self.env.cr: Đây là con trỏ cơ sở dữ liệu self.env.user: Đây là người dùng thực thi hành động self.env.context: Đây là ngữ cảnh, là một từ điển Python chứa nhiều thông tin như ngôn ngữ của người dùng, múi giờ được cấu hình và các thông tin khác các khóa cụ thể có thể được đặt vào thời gian chạy bởi các hành động của giao diện người dùng Khi viết một phương thức mới, bạn thường sẽ sử dụng @ api.multi. Ngoài việc tạo môi trường, trình trang trí này yêu cầu lớp RPC tự khởi tạo bằng các ID được cung cấp trong các id đối số RPC. Trong các phương thức như vậy, self là một recordset có thể tham chiếu đến một số lượng các bản ghi cơ sở dữ liệu tùy ý. Bộ trang trí @ api.model tương tự nhưng được sử dụng trên các phương thức mà chỉ có mô hình là quan trọng, không phải nội dung của recordset, mà không được tác động theo phương thức. Khái niệm này tương tự như trang trí @classmethod của Python (nhưng chúng ta thường không thể sử dụng nó trong các mô hình Odoo vì chúng ta cần truy cập vào self.env và các thuộc tính quan trọng khác).
10
Đây là đoạn mã ví dụ có tên là change_state (): # return_book_ids là danh sách các id sách để trả lại books = self.env ['library.book'] books.browse (return_book_ids) .change_state ('có sẵn') Khi change_state () được gọi, self là một recordset (có thể trống) chứa các bản ghi của mô hình library.book. Phần thân của phương thức change_state () lặp lại để tự xử lý từng cuốn sách trong recordset. Looping trên tự nhìn có vẻ lạ lúc đầu, nhưng bạn sẽ quen với mẫu này rất nhanh. Bên trong vòng lặp, hàm change_state () gọi là is_allowed_transition (). Các cuộc gọi được thực hiện bằng cách sử dụng cuốn sách biến địa phương, nhưng nó có thể đã được thực hiện trên bất kỳ recordset cho library.book mô hình, bao gồm, ví dụ, self, kể từ is_allowed_transition () được trang trí với @ api.model. Nếu việc chuyển đổi được cho phép, change_state () gán trạng thái mới cho cuốn sách bằng cách gán một giá trị cho thuộc tính của recordset. Điều này chỉ hợp lệ trên các bản ghi có độ dài 1, được đảm bảo là trường hợp khi lặp lại self.
11
Còn nữa… Có một số yếu tố quan trọng đáng để hiểu.
Ẩn các phương thức từ giao diện RPC Trong API cũ, các phương thức có tên được đặt trước bởi dấu gạch dưới không được hiển thị thông qua giao diện RPC và do đó không được gọi bởi trình khách web. Đây vẫn là trường hợp với API mới, cũng cung cấp một cách khác để làm cho một phương thức không có sẵn cho giao diện RPC; nếu bạn không đặt một trang trí @ api.model hoặc @ api.multi vào nó (hoặc trên một trong các trình trang trí được đề cập trong mã API cũ Porting đến công thức API mới trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao), thì phương thức này sẽ không thể gọi bằng các phần mở rộng của mô hình bằng cách sử dụng API truyền thống cũng như không qua RPC. Trình trang trí @ api.one 12
Bạn có thể gặp phải trình trang trí @ api.one trong khi đọc mã nguồn. Trong Odoo 9.0, trang trí này không được chấp nhận vì hành vi của nó có thể gây nhầm lẫn - ngay từ cái nhìn đầu tiên, và biết về @ api.multi, có vẻ như trình trang trí này cho phép phương thức chỉ được gọi trên các bản ghi có kích thước 1, nhưng không. Khi nói đến độ dài recordset, @ api.one tương tự như @api.muti, nhưng nó thực hiện một vòng lặp for trên recordset bên ngoài phương thức và tổng hợp giá trị trả về của mỗi lần lặp của vòng lặp trong một danh sách, được trả về cho người gọi. Tránh sử dụng nó trong mã của bạn.
Xem thêm Trong chương 8, khung nhìn backend, hãy tham khảo thêm nút để tạo công thức để tìm hiểu cách gọi một phương thức như vậy từ giao diện người dùng. Bạn sẽ tìm thấy thông tin bổ sung về các trang trí được định nghĩa trong openerp.api trong công thức từ Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao: Chuyển mã API cũ sang API mới.
2. Báo cáo lỗi cho người dùng 13
Trong quá trình thực hiện phương thức, đôi khi cần phải hủy bỏ việc xử lý vì một điều kiện lỗi đã được đáp ứng. Công thức này cho thấy cách thực hiện điều này sao cho thông báo lỗi hữu ích được hiển thị cho người dùng khi phương thức ghi tệp vào đĩa gặp phải lỗi.
Sẵn sàng Để sử dụng công thức này, bạn cần một phương pháp, có thể có một tình trạng bất thường. Chúng tôi sẽ sử dụng như sau: import os from openerp import models, fields, api class SomeModel (models.Model): data = fields.Text ('Dữ liệu') @ api.multi def save (self, tên tập tin): path = os.path.join ('/ opt / exports', tên tệp) with open (đường dẫn, 'w') là fobj: 14
for bản ghi in self: fobj.write (record.data) fobj.write ('\ n') Phương pháp này có thể thất bại vì các vấn đề về quyền, hoặc một đĩa đầy đủ, hoặc một tên bất hợp pháp, điều này sẽ gây ra một ngoại lệ IOError hoặc OSError được nâng lên.
2. Sửa đổi phương thức để bắt ngoại lệ được nêu ra và nâng cao một ngoại lệ UserError: @ api.multi def save (self, tên tập tin): nếu '/' trong tên tệp hoặc '\\' trong tên tệp: tăng UserError (tên tệp 'Tên tập tin bất hợp pháp% s'%) path = os.path.join ('/ opt / exports', tên tệp) thử: 15
với mở (đường dẫn, 'w') là fobj: for bản ghi in self: fobj.write (record.data) fobj.write ('\ n') ngoại trừ (IOError, OSError) như exc: message = 'Không thể lưu tệp:% s'% exc raise UserError (tin nhắn)
Làm thế nào nó hoạt động… Khi một ngoại lệ được nâng lên trong Python, nó sẽ lan truyền ngăn xếp cuộc gọi cho đến khi nó được xử lý. Trong Odoo, lớp RPC trả lời các cuộc gọi được thực hiện bởi máy khách web bắt tất cả ngoại lệ và, tùy thuộc vào lớp ngoại lệ, nó sẽ kích hoạt các hành vi có thể khác nhau trên máy khách web. Bất kỳ ngoại lệ nào không được định nghĩa trong openerp.exceptions sẽ được xử lý như một Lỗi Máy chủ Nội bộ (trạng thái HTTP 500), với dấu vết ngăn xếp. Một UserError sẽ hiển thị một thông báo lỗi trong giao diện người dùng. Mã của công thức thay đổi OSError thành UserError để đảm bảo thông báo được hiển thị một cách thân thiện. Trong mọi trường hợp, giao dịch cơ sở dữ liệu hiện tại được khôi phục. 16
Tất nhiên, nó không bắt buộc phải có ngoại lệ với một try..except, xây dựng để nâng cao một ngoại lệ UserError. Nó là hoàn toàn OK để kiểm tra cho một số điều kiện, chẳng hạn như sự hiện diện của các ký tự bất hợp pháp trong một tên tập tin, và để nâng cao ngoại lệ khi kiểm tra đó là True. Điều này sẽ ngăn chặn việc xử lý thêm yêu cầu của người dùng.
Còn nữa… Có thêm một vài lớp ngoại lệ được định nghĩa trong openerp.exceptions, tất cả đều lấy ra lớp cơ sở ngoại lệ except_orm. Hầu hết chúng chỉ được sử dụng trong nội bộ, ngoài những điều sau đây: Cảnh báo: Trong Odoo 8.0, openerp.exceptions.Warning đóng vai trò của UserError trong 9.0. Nó bây giờ không được chấp nhận vì tên là lừa đảo (nó là một lỗi, không phải là một cảnh báo) và nó va chạm với lớp Cảnh báo dựng sẵn Python. Nó chỉ được giữ cho khả năng tương thích ngược và bạn nên sử dụng UserError trong 9.0. ValidationError: Ngoại lệ này được nâng lên khi một ràng buộc Python trên một trường không được tôn trọng. Trong Chương 4, Các mô hình ứng dụng, hãy tham khảo thêm các ràng buộc ràng buộc vào một công thức Mô hình để biết thêm thông tin
17
3. Lấy một recordset rỗng cho một mô hình khác Khi viết mã Odoo, các phương thức của mô hình hiện tại có sẵn thông qua self. Nếu bạn cần làm việc trên một mô hình khác, không thể trực tiếp khởi tạo lớp của mô hình đó - bạn cần phải có một bản ghi cho mô hình đó để bắt đầu làm việc. Công thức này cho thấy làm thế nào để có được một recordset trống cho bất kỳ mô hình đăng ký trong Odoo bên trong một phương thức mô hình.
Sẵn sàng Công thức này sẽ sử dụng lại thiết lập của ví dụ thư viện trong mô-đun addon my_module. Chúng tôi sẽ viết một phương pháp nhỏ trong thư viện.biểu mẫu tìm kiếm tất cả thư viện. các thành viên. Để làm điều này, chúng ta cần lấy một recordset rỗng cho thư viện.members. 18
Làm nó thế nào… Để có được một recordset cho các library.members trong một phương thức của library.book, bạn cần thực hiện các bước sau: 1. Trong lớp LibraryBook, viết một phương thức gọi là get_all_library_members: class LibraryBook (models.Model): # ... @ api.model def get_all_library_members (tự): # ... 2. Trong phần thân của phương thức, sử dụng đoạn mã sau: library_member_model = self.env ['library.member'] return library_member_model.search ([])
Luc nạy muon goi récordsét librạry.mémbérs Sélf.gét_ạll_libạry_mémbérs( ) 19
cho
Làm thế nào nó hoạt động… Khi khởi động, Odoo tải tất cả các mô-đun và kết hợp các lớp khác nhau bắt nguồn từ Mô hình và xác định hoặc mở rộng một mô hình cụ thể. Các lớp này được lưu trữ trong sổ đăng ký Odoo được lập chỉ mục theo tên. Môi trường trong self.env cung cấp khả năng truy cập vào registry bằng cách mô phỏng một từ điển Python; nếu bạn biết tên của mô hình bạn đang tìm kiếm, self.env [model_name] sẽ cho bạn một tập bản ghi trống cho mô hình đó. Hơn nữa, recordset sẽ chia sẻ môi trường của self. Cuộc gọi search () được giải thích trong công thức Tìm kiếm hồ sơ sau này.
Xem thêm Thay đổi người dùng thực hiện một hành động và gọi một phương thức với một công thức môi trường đã được sửa đổi trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao, xử lý việc sửa đổi self.env khi chạy.
20
4. Tạo bản ghi mới Một nhu cầu thường xuyên khi viết các phương thức logic nghiệp vụ là tạo các bản ghi mới. Công thức này giải thích cách tạo bản ghi của mô hình res.partner, được định nghĩa trong mô đun addon cơ bản của Odoo. Chúng tôi sẽ tạo một đối tác mới đại diện cho một công ty, với một số liên hệ.
Sẵn sàng Bạn cần phải biết cấu trúc của các mô hình mà bạn muốn tạo một bản ghi, đặc biệt là tên và loại của chúng cũng như bất kỳ ràng buộc nào tồn tại trên các trường này (ví dụ, cho dù một số trong số đó là bắt buộc). Mô hình res.partner được định nghĩa trong Odoo có số lượng trường rất lớn và để đơn giản, chúng tôi 21
sẽ chỉ sử dụng một vài trong số các trường này. Hơn nữa, định nghĩa mô hình trong Odoo sử dụng API cũ. Để giúp bạn theo dõi công thức, đây là một cổng của định nghĩa mô hình mà chúng tôi sẽ sử dụng cho API mới:
Child_ids co tén trong modél lạ Contạcts vạ giạ tri lạ tạt cạ cạc Id bạn ghi cuạ modél rés.pạrtnér lớp ResPartner (models.Model): _name = 'res.partner' name = fields.Char ('Name', required = True) email = fields.Char ('Email') date = fields.Date ('Date') is_company = fields.Boolean ('Là một công ty') parent_id = fields.Many2one ('res.partner', 'Công ty liên quan') child_ids = fields.One2many ('res.partner', 'parent_id', 'Liên hệ')
22
Làm thế nào để làm nó… Để tạo đối tác với một số liên hệ, bạn cần thực hiện các bước sau: 1. Bên trong phương thức cần tạo đối tác mới, lấy ngày hiện tại được định dạng là chuỗi được mong đợi bằng create (): today_str = fields.Date.context_today () 2. Chuẩn bị từ điển các giá trị cho các trường của liên hệ đầu tiên: val1 = {'name': u'Eric Idle ', 'email': u'
[email protected] ' 'date': today_str} 3. Chuẩn bị từ điển các giá trị cho các trường của liên hệ thứ hai: val2 = {'tên': u'John Cleese ', 'email': u'
[email protected] ', 'date': today_str}
23
4. Chuẩn bị từ điển các giá trị cho các lĩnh vực của công ty: partner_val = { 'name': u'Flying Circus ', 'email': u'
[email protected] ', 'ngày': today_str, 'is_company': Đúng, 'child_ids': [(0, 0, val1), (0, 0, val2), ] } 5. Gọi phương thức create () để tạo các bản ghi mới: record = self.env ['res.partner']. tạo (partner_val)
24
Làm thế nào nó hoạt động… Để tạo một bản ghi mới cho một mô hình, chúng ta có thể gọi phương thức create (values) trên bất kỳ recordset liên quan đến mô hình. Phương thức này trả về một tập bản ghi mới có độ dài 1 chứa bản ghi mới, với các giá trị trường được chỉ định trong từ điển giá trị. Trong từ điển: Các giá trị trường văn bản được cho bằng các chuỗi Python (tốt nhất là các chuỗi Unicode). Các giá trị trường Float và Integer được cho bằng cách sử dụng các float hoặc các số nguyên của Python. Các giá trị trường Boolean được đưa ra, tốt nhất là sử dụng boolean Python hoặc số nguyên. Các giá trị trường Date (resp. Datetime) được cho là các chuỗi Python. Sử dụng fields.Date.to_string () (resp. fields.Datetime.to_string ()) để 25
chuyển đổi một Python datetime.date (resp. datetime.datetime) đối tượng với định dạng mong muốn. Các giá trị trường nhị phân được truyền dưới dạng chuỗi được mã hóa Base64. Mô-đun base64 từ thư viện chuẩn Python cung cấp các phương thức như mã hóa (encodestring) mã hóa một chuỗi trong Base64. Các giá trị trường Many2one được cho với một số nguyên, mà phải là ID cơ sở dữ liệu của hồ sơ liên quan. Các trường One2many và Many2many sử dụng cú pháp đặc biệt. Giá trị là danh sách chứa bộ ba phần tử, như sau: Tuple Hiệu ứng (0, 0, dict_val) Tạo một bản ghi mới sẽ liên quan đến bản ghi chính (6, 0, id_list) Tạo mối quan hệ giữa bản ghi được tạo và tồn tại các bản ghi có ID trong danh sách id_list của Python Thận trọng: Khi được sử dụng trên One2many, điều này sẽ xóa các bản ghi từ bất kỳ mối quan hệ trước Trong công thức, chúng tôi tạo từ điển cho hai liên hệ trong công ty mà chúng tôi muốn tạo, và sau đó chúng tôi sử dụng các từ điển này trong mục nhập child_ids của từ điển cho công ty được tạo ra, sử dụng cú pháp (0, 0, dict_val) đã giải thích trước đó. Khi create () được gọi trong bước 5, ba bản ghi được tạo: Một cho công ty đối tác chính, được trả lại bằng cách tạo Một cho hai liên hệ, có sẵn trong record.child_ids
26
Còn nữa Nếu mô hình đã xác định một số giá trị mặc định cho một số trường, thì không có gì cần phải thực hiện; create () sẽ xử lý việc tính toán các giá trị mặc định cho các trường không có trong từ điển được cung cấp. Mặt khác, các phương thức onchange không được gọi bởi create (), bởi vì chúng được gọi bởi máy khách web trong phiên bản đầu tiên của bản ghi. Một số phương pháp này tính giá trị mặc định cho các trường liên quan đến một trường nhất định. Khi tạo các bản ghi bằng tay, bạn phải tự làm việc, bằng cách cung cấp các giá trị rõ ràng hoặc bằng cách gọi các phương thức onchange. Các phương thức Calling onchange trên công thức phía máy chủ trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao giải thích cách thực hiện điều này.
27
5. Cập nhật giá trị của bản ghi recordset Logic nghiệp vụ có nghĩa là cập nhật thường xuyên các bản ghi với thay đổi các giá trị của một số trường của chúng. Công thức này cho thấy cách thêm liên hệ cho đối tác và sửa đổi trường ngày của đối tác khi chúng tôi đi.
Sẵn sàng Công thức này sẽ sử dụng định nghĩa res.partner được đơn giản hóa giống như công thức Tạo bản ghi mới trước đó. Bạn có thể tham khảo định nghĩa đơn giản này để biết các trường. Trường ngày của res.partner không có nghĩa xác định trong Odoo. Để minh họa cho mục đích, chúng tôi sẽ sử dụng điều này để ghi lại ngày hoạt động trên đối tác, do đó việc tạo địa chỉ liên hệ mới sẽ cập nhật ngày của đối tác.
28
Làm thế nào để làm nó… Để cập nhật đối tác, bạn có thể viết một phương thức mới có tên là add_contacts () được định nghĩa như sau: @ api.model def add_contacts (self, partner, contacts): partner.ensure_one () nếu liên hệ: partner.date = fields.Date.context_today () partner.child_ids | = liên hệ
Làm thế nào nó hoạt động… Phương thức này bắt đầu bằng cách kiểm tra xem đối tác được truyền dưới dạng đối số có chứa chính xác một bản ghi hay không bằng cách gọi hàm sure_one (). Phương thức này sẽ đưa ra một ngoại lệ nếu đây không phải là 1 bản ghi quá trình xử lý sẽ bị hủy bỏ. Điều này là cần thiết, vì chúng tôi không thể thêm cùng địa chỉ liên lạc vào một số đối tác cùng một lúc. Sau đó, phương thức kiểm tra contacts không rỗng, bởi vì chúng tôi không muốn cập nhật ngày của đối tác nếu chúng ta không sửa đổi nó. 29
Cuối cùng, phương thức sửa đổi các giá trị của các thuộc tính của bản ghi đối tác. Vì child_ids là quan hệ One2many, giá trị của nó là một recordset. Chúng tôi sử dụng toán tử | = để liên kết các địa chỉ liên hệ hiện tại của đối tác và các địa chỉ liên hệ mới. Xem hướng dẫn sau, Kết hợp các tập bản ghi, để biết thêm thông tin về các toán tử này. Lưu ý rằng không có giả định nào được thực hiện trên self. Phương thức này có thể được định nghĩa trên bất kỳ lớp mô hình nào.
30
31
Còn nữa… Có ba tùy chọn có sẵn nếu bạn muốn viết giá trị mới cho các trường của bản ghi. Tùy chọn 1 là một trong những giải thích trong công thức, và nó hoạt động trong mọi ngữ cảnh - gán các giá trị trực tiếp trên thuộc tính thể hiện trường của bản ghi. Không thể gán giá trị cho tất cả các phần tử recordset trong một lần, vì vậy bạn cần lặp lại trên recordset, trừ khi bạn chắc chắn rằng bạn chỉ đang xử lý một bản ghi duy nhất. Tùy chọn 2 là sử dụng phương thức update () bằng cách chuyển một tên trường ánh xạ từ điển tới các giá trị bạn muốn đặt. Điều này cũng chỉ hoạt động cho các bản ghi có độ dài 1. Nó có thể tiết kiệm một số gõ khi bạn cần cập nhật các giá trị của một số trường cùng một lúc trên cùng một bản ghi. Đây là bước 2 của công thức, viết lại để sử dụng tùy chọn này: @ api.model def add_contacts (self, partner, contacts): partner.ensure_one () nếu liên hệ: today = fields.Date.context_today () partner.update ( {'date': hôm nay, 'child_ids': partner_child_ids | địa chỉ liên hệ} ) 32
Tùy chọn 3 là gọi phương thức write (), chuyển một tên trường ánh xạ từ điển tới các giá trị bạn muốn đặt. Phương pháp này hoạt động cho các bản ghi có kích thước tùy ý và sẽ cập nhật tất cả bản ghi với các giá trị được chỉ định trong một thao tác cơ sở dữ liệu đơn lẻ khi hai tùy chọn thực hiện một cuộc gọi cơ sở dữ liệu cho mỗi bản ghi và mỗi trường. Tuy nhiên, nó có một số hạn chế: Nó không hoạt động nếu các bản ghi chưa có trong cơ sở dữ liệu (xem phần Write) các phương thức onchange trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao cho biết thêm thông tin về điều này) Nó yêu cầu sử dụng một định dạng đặc biệt khi viết các trường quan hệ, tương tự như trường được sử dụng bởi phương thức create () Tuple (0, 0, dict_val) (1, id, dict_val) (2, id) (3, id) (4, id) (5,) (6, 0, id_list)
Hiệu ứng Điều này tạo một bản ghi mới sẽ liên quan đến bản ghi chính. Điều này cập nhật các bản ghi liên quan với ID được chỉ định với các giá trị được cung cấp. Điều này loại bỏ bản ghi với ID được chỉ định từ các bản ghi có liên quan và xóa nó khỏi cơ sở dữ liệu. Điều này loại bỏ bản ghi với ID được chỉ định từ các bản ghi liên quan. Bản ghi sẽ không bị xóa khỏi cơ sở dữ liệu. Điều này thêm một bản ghi hiện có với ID được cung cấp vào danh sách các bản ghi liên quan. Điều này loại bỏ tất cả các hồ sơ liên quan, tương đương với việc gọi cho mỗi id có liên quan. Điều này tạo ra một mối quan hệ giữa bản ghi được cập nhật và bản ghi hiện có, có ID của họ trong danh sách id_list Python.
Tại thời điểm viết bài này, tài liệu chính thức đã lỗi thời và đề cập đến các số hoạt động 3, 4, 5 và 6 không có sẵn trên các trường One2many, điều này không còn đúng nữa. Tuy nhiên, một số trong số này có thể không hoạt động với các trường One2many, tùy thuộc vào các ràng buộc trên các mô hình; ví dụ, nếu mối quan hệ Many2one ngược lại là bắt buộc, thì thao tác 3 sẽ thất bại vì nó sẽ dẫn đến một mối quan hệ Many2one chưa được đặt. 33
6. Tìm kiếm bản ghi Tìm kiếm các bản ghi cũng là một hoạt động phổ biến trong các phương thức logic nghiệp vụ. Công thức này cho thấy cách tìm tất cả các công ty Đối tác và địa chỉ liên hệ của họ theo tên công ty. Sẵn sàng Công thức này sẽ sử dụng định nghĩa res.partner được đơn giản hóa giống như công thức Tạo bản ghi mới trước đó. Bạn có thể tham khảo định nghĩa đơn giản này để biết các trường. Chúng ta sẽ viết mã trong một phương thức gọi là find_partners_and_contact (self, name).
34
Làm thế nào để làm nó… Để tìm đối tác, bạn cần thực hiện các bước sau: 1. Nhận một recordset trống cho res.partner: @ api.model def find_partners_and_contacts (tự, tên): partner = self.env ['res.partner'] 2. Viết tên miền tìm kiếm cho tiêu chí của bạn: domain = ['|', '&', ('is_company', '=', Đúng), ('tên', 'thích', tên), '&', ('is_company', '=', False), ('parent_id.name', 'like', tên)] 35
3. Gọi phương thức search () với miền và trả về recordset: return partner.search (tên miền)
Làm thế nào nó hoạt động… Bước 1 định nghĩa phương thức. Vì chúng ta không sử dụng nội dung của bản thân, chúng ta trang trí nó với @ api.model, nhưng điều này không liên quan đến mục đích của công thức này. Sau đó, chúng tôi nhận được một sản phẩm nào recordset cho mô hình res.partner vì chúng ta cần nó để tìm kiếm hồ sơ res.partner. 36
Bước 2 tạo một miền tìm kiếm trong một biến cục bộ. Thông thường, bạn sẽ thấy việc tạo này được nêu trong gọi để tìm kiếm, nhưng với các tên miền phức tạp, thực hành tốt là xác định nó một cách riêng biệt. Để có giải thích đầy đủ về cú pháp miền tìm kiếm, vui lòng tham khảo bộ lọc Xác định trên danh sách bản ghi: Công thức tên miền trong Chương 8, Chế độ xem phụ trợ. Bước 3 gọi phương thức search () với miền. Phương thức trả về một recordset chứa tất cả các bản ghi khớp với tên miền, sau đó có thể xử lý thêm. bên trong công thức, chúng tôi gọi phương thức chỉ với tên miền, nhưng các đối số từ khóa sau đây là cũng được hỗ trợ: offset = N: Điều này được sử dụng để bỏ qua N bản ghi đầu tiên khớp với truy vấn. Điều này có thể được sử dụng cùng với giới hạn để thực hiện phân trang hoặc để giảm bộ nhớ tiêu thụ khi xử lý một số lượng lớn các bản ghi. Giá trị mặc định là 0. limit = N: trả về tối đa N bản ghi. Theo mặc định, không có giới hạn. order = sort_specification: Điều này được sử dụng để buộc thứ tự trên trả về recordset. Theo mặc định, thứ tự được đưa ra bởi thuộc tính _order của lớp mô hình. count = boolean: Nếu True, hàm này trả về số lượng bản ghi thay vì recordset. Nó mặc định là Sai.
Chúng tôi khuyên bạn nên sử dụng phương thức search_count (tên miền) thay vì tìm kiếm (tên miền, count = True), như tên của phương thức truyền tải hành vi theo cách rõ ràng hơn nhiều; cả hai sẽ cho kết quả tương tự.
37
Còn nữa… Chúng tôi đã nói rằng phương thức search () trả về tất cả các bản ghi khớp với tên miền. Điều này không hoàn toàn đúng. Phương thức này đảm bảo rằng chỉ các bản ghi mà người dùng thực hiện tìm kiếm có quyền truy cập được trả về. Ngoài ra, nếu mô hình có trường boolean được gọi là hoạt động và không có thuật ngữ nào của miền tìm kiếm chỉ định điều kiện trên trường đó, thì điều kiện ẩn được thêm bằng tìm kiếm để chỉ trả về hoạt động = Bản ghi thực. Vì vậy, nếu bạn mong đợi một tìm kiếm để trả lại một cái gì đó nhưng bạn chỉ nhận được bản ghi trống, hãy chắc chắn để kiểm tra giá trị của trường hoạt động (nếu có) và để kiểm tra các quy tắc ghi lại. Xem công thức Gọi phương thức với ngữ cảnh khác trong Chương 6, Kỹ thuật phát triển phía máy chủ nâng cao để biết cách không có hoạt động ngầm = Điều kiện thực được thêm vào. Xem phần Truy cập bản ghi giới hạn bằng cách sử dụng công thức quy tắc ghi trong Chương 10, Truy cập bảo mật để biết thêm thông tin về các quy tắc truy cập cấp bản ghi. 38
Nếu vì một số lý do bạn thấy mình viết các truy vấn SQL thô để tìm ID bản ghi, hãy chắc chắn sử dụng self.env ['record.model']. Tìm kiếm ([('id', 'in', tuple (ids)). Sau truy xuất các ID để đảm bảo rằng các quy tắc bảo mật được áp dụng. Điều này đặc biệt quan trọng trong các trường hợp đa nhiệm Odoo trong đó các quy tắc ghi lại được sử dụng để đảm bảo phân biệt đối xử giữa các công ty.
7. Kết hợp recordsets Đôi khi, bạn sẽ thấy rằng bạn đã thu được các tập bản ghi không chính xác những gì bạn cần. Công thức này cho thấy nhiều cách khác nhau để kết hợp chúng. Sẵn sàng Để sử dụng công thức này, bạn cần có hai hoặc nhiều tập bản ghi cho cùng một mô hình.
39
Làm thế nào để làm nó… Đây là cách để đạt được các thao tác phổ biến trên các tập bản ghi: 1. Để hợp nhất hai recordsets thành một trong khi vẫn giữ nguyên thứ tự của chúng, sử dụng thao tác sau: result = recordset1 + recordset2 2. Để hợp nhất hai tập bản ghi vào một bộ đảm bảo không có kết quả trùng lặp trong kết quả, hãy sử dụng thao tác sau: result = recordset1 | recordset2 3. Để tìm các bản ghi chung cho hai bộ ghi, sử dụng thao tác sau: result = recordset1 & recordset2
40
Làm thế nào nó hoạt động… Lớp cho các tập bản ghi thực hiện các định nghĩa lại toán tử Python khác nhau, được sử dụng ở đây. Dưới đây là bảng tóm tắt các toán tử Python hữu ích nhất có thể được sử dụng trên các bản ghi:
Toán tử R1 + R2
Đã thực hiện hành động Điều này trả về một recordset mới chứa các bản ghi từ R1, sau đó là bản ghi từ R2. Điều này có thể tạo bản ghi trùng lặp trong recordset. 41
R1 - R2 R1 & R2
R1 | R2
R1 ==R2 R1<=R2 R1 in R2 R1 >= R2 R2 in R1 R1 != R2
Điều này trả về một recordset mới bao gồm các bản ghi từ R1, mà không phải là trong R2. Thứ tự được bảo quản. Điều này trả về một recordset mới với tất cả các bản ghi thuộc về cả R1 và R2(giao điểm của các tập bản ghi). Thứ tự không được bảo quản ở đây. R1 | R2 Điều này trả về một recordset mới với các bản ghi thuộc về R1 hoặc R2 (liên hiệp các tập bản ghi). Đơn đặt hàng không được bảo tồn, nhưng không có bản sao. Đúng nếu cả hai tập bản ghi chứa cùng một bản ghi. Đúng nếu tất cả các bản ghi trong R1 cũng trong R2. Cả hai cú pháp đều tương đương nhau. Đúng nếu tất cả các bản ghi trong R2 cũng trong R1. Cả hai cú pháp đều tương đương nhau. Đúng nếu R1 và R2 không chứa cùng một bản ghi.
Cũng có các toán tử tại chỗ + =, - =, & =, và | =, sửa đổi toán hạng bên trái thay vì tạo một recordset mới. Chúng rất hữu ích khi cập nhật bản ghi Các trường One2many hoặc Many2many. Xem phần Cập nhật giá trị của bản ghi hồ sơ công thức trước đó cho một ví dụ.
Còn nữa… 42
Phương thức sort () sẽ sắp xếp các bản ghi trong một recordset. Được gọi không có đối số, thuộc tính _order của mô hình sẽ được sử dụng. Nếu không, một hàm có thể được chuyển để tính toán một khóa so sánh theo cùng một kiểu với hàm được sắp xếp (trình tự, khóa) được tích hợp sẵn của Python. Đối số từ khóa đảo ngược cũng được hỗ trợ. Lưu ý về hiệu suất :Khi tham số _order mặc định của mô hình được sử dụng, việc phân loại được ủy quyền cho cơ sở dữ liệu và một hàm SELECT mới được thực hiện để có được thứ tự. Nếu không, sắp xếp được thực hiện bởi Odoo. Tùy thuộc vào những gì đang được thao tác, và về kích thước của các recordsets, có thể có một số khác biệt hiệu suất quan trọng.
8. Lọc các tập bản ghi Trong một số trường hợp, bạn đã có một recordset, nhưng bạn chỉ cần hoạt động trên một số bản ghi.
43
Tất nhiên, Bạn có thể lặp lại trên recordset, kiểm tra các điều kiện trên mỗi lần lặp lại và hành động tùy thuộc vào kết quả kiểm tra. Nó có thể dễ dàng hơn, và trong một số trường hợp, hiệu quả hơn để xây dựng một recordset mới chỉ chứa các bản ghi thú vị và gọi một thao tác đơn lẻ trên recordset đó. Công thức này cho thấy cách sử dụng phương thức filter () để trích xuất một recordset từ một tệp khác.
Sẵn sàng Chúng tôi sẽ sử dụng lại mô hình res.partner được đơn giản hóa được hiển thị trong công thức Tạo hồ sơ mới trước đây. Công thức này định nghĩa một phương thức để trích xuất các đối tác có một địa chỉ email từ một recordset được cung cấp.
Làm thế nào để làm nó…
44
Để trích xuất các bản ghi với một địa chỉ e-mail từ một recordset, bạn cần thực hiện các bước sau: 1. Xác định phương thức chấp nhận bản ghi gốc: @ api.model def partners_with_email (tự, đối tác): 2. Xác định hàm vị từ bên trong: def predicate (đối tác): if partner.email: return True return false 3. Bộ lọc cuộc gọi () như sau: return partners.filter (vị ngữ)
Làm thế nào nó hoạt động… Việc thực hiện phương thức filter () của recordsets tạo ra một recordset rỗng, trong đó nó thêm tất cả các bản ghi mà hàm predicate đánh giá là True. Bản ghi mới cuối cùng cũng được trả về. Thứ tự các bản ghi trong bản ghi gốc được giữ nguyên. 45
Công thức trước đã sử dụng hàm nội bộ có tên. Đối với các vị từ đơn giản như vậy, bạn thường sẽ tìm thấy một hàm lambda ẩn danh được sử dụng: @ api.model def partners_with_email (tự, đối tác): return partners.filter (lambda p: p.email) Trên thực tế, để lọc một recordset dựa trên thực tế là một thuộc tính là trung thực theo nghĩa Python, bạn có thể sử dụng partners.filter ('email').
Còn nữa… Hãy nhớ rằng bộ lọc () hoạt động trong bộ nhớ. Nếu bạn đang cố gắng tối ưu hóa hiệu suất của một phương thức trên đường dẫn tới hạn, bạn có thể muốn sử dụng một miền tìm kiếm hoặc thậm chí chuyển sang SQL, với chi phí dễ đọc.
46
9. Traversing bản ghi quan hệ Khi làm việc với tập bản ghi độ dài 1, các trường khác nhau có sẵn dưới dạng thuộc tính bản ghi. Các thuộc tính quan hệ (One2many, Many2one và Many2many) cũng có sẵn với các giá trị là các bản ghi. Khi làm việc với các tập bản ghi có nhiều hơn một bản ghi, các thuộc tính không thể được sử dụng. Công thức này cho thấy cách sử dụng phương thức ánh xạ () để duyệt các quan hệ recordset; chúng tôi sẽ viết hai phương pháp thực hiện các hoạt động sau: Lấy lại e-mail của tất cả các liên hệ của một công ty đối tác được chuyển giao làm đối số Lấy các công ty khác nhau mà một số đối tác liên hệ có liên quan
Sẵn sàng 47
Chúng tôi sẽ sử dụng lại mô hình Đối tác đơn giản được hiển thị trong công thức Tạo bản ghi mới của chương này.
Làm thế nào để làm nó… Để viết các phương thức thao tác đối tác, bạn cần thực hiện các bước sau: 1. Xác định phương thức được gọi là get_email_addresses (): @ api.model def get_email_addresses (tự, đối tác): partner.ensure_one () 2. Gọi ánh xạ () để nhận địa chỉ e-mail của các số liên lạc của đối tác: return partner.mapped ('child_ids.email') 3. Xác định một phương thức gọi là get_companies (): @ api.model def get_companies (bản thân, đối tác): 4. Call ánh xạ () để có được các công ty khác nhau của các đối tác: 48
return partners.mapped ('parent_id')
Làm thế nào nó hoạt động… Ở bước 1, chúng tôi gọi Ensure_one () để đảm bảo rằng chúng tôi có một đối tác duy nhất. Điều này không cần thiết cho công thức, vì ánh xạ () hoạt động rất tốt trên các tập bản ghi có kích thước tùy ý. Tuy nhiên, nó được yêu cầu bởi đặc điểm kỹ thuật của phương thức chúng tôi đang viết, vì chúng tôi không muốn truy xuất danh bạ từ nhiều công ty do nhầm lẫn. Trong bước 2 và bước 4, chúng ta gọi phương thức ánh xạ (đường dẫn) để đi qua các trường của recordset; path là một chuỗi chứa các tên trường được phân 49
tách bằng dấu chấm. Đối với mỗi trường trong đường dẫn, ánh xạ () tạo một bản ghi mới chứa tất cả các bản ghi liên quan đến trường này cho tất cả các phần tử trong bản ghi hiện tại và sau đó áp dụng phần tử tiếp theo trong đường dẫn trên bản ghi mới đó. Nếu trường cuối cùng trong đường dẫn là một trường quan hệ, ánh xạ () sẽ trả về một bản ghi; nếu không, danh sách Python sẽ được trả về. Phương thức ánh xạ () có hai đặc tính nổi bật: Nếu đường dẫn là một tên trường vô hướng duy nhất, thì danh sách được trả về theo thứ tự giống như bản ghi đã xử lý. Nếu đường dẫn chứa trường quan hệ, thì thứ tự không được giữ nguyên, nhưng các bản sao được loại bỏ khỏi kết quả. Thuộc tính thứ hai này rất hữu ích trong một phương thức được trang trí với @ api.multi nơi bạn muốn thực hiện một thao tác trên tất cả các bản ghi được chỉ bởi một trường Many2many của tất cả bản ghi, nhưng cần đảm bảo rằng hành động được thực hiện chỉ một lần ( ngay cả khi hai hồ sơ tự chia sẻ cùng một bản ghi đích).
Còn nữa… Khi sử dụng ánh xạ (), hãy nhớ rằng nó hoạt động trong bộ nhớ bên trong máy chủ Odoo bằng cách lặp đi lặp lại các quan hệ và do đó tạo các truy vấn SQL, 50
có thể không hiệu quả; tuy nhiên, mã này là ngắn gọn và biểu cảm. Nếu bạn đang cố gắng tối ưu hóa một phương thức trên đường dẫn quan trọng của việc thực hiện cá thể của bạn, bạn có thể muốn viết lại lời gọi tới ánh xạ () và biểu diễn nó như một tìm kiếm () với miền thích hợp, hoặc thậm chí chuyển sang SQL (tại chi phí dễ đọc). Phương thức ánh xạ () cũng có thể được gọi với một hàm làm đối số. Trong trường hợp này, nó trả về một danh sách chứa kết quả của hàm được áp dụng cho mỗi bản ghi của bản thân hoặc liên kết của các bản ghi được hàm trả về, nếu hàm trả về một bản ghi.
Xem thêm Công thức tìm kiếm hồ sơ Việc thực thi các truy vấn SQL nguyên truy vấn Trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao
51
10. Mở rộng logic nghiệp vụ được định nghĩa trong một Mô hình Khi xác định một mô hình mở rộng một mô hình khác, nó thường là cần thiết để tùy chỉnh hành vi của một số phương thức được xác định trên mô hình ban đầu. Đây là một nhiệm vụ rất dễ dàng trong Odoo và là một trong những tính năng mạnh mẽ nhất của khung công tác cơ bản. Chúng ta sẽ chứng minh điều này bằng cách mở rộng một phương thức tạo bản ghi để thêm một trường mới trong các bản ghi đã tạo.
Sẵn sàng Nếu bạn muốn theo dõi công thức, hãy đảm bảo bạn có addon my_module từ Chương 3, Tạo Mô-đun Odoo, với thuật sĩ cho vay được định nghĩa trong Trình 52
hướng dẫn viết để hướng dẫn công thức người dùng từ Chương 6, Kỹ thuật Phát triển Phía Máy chủ Nâng cao. Tạo một mô-đun addon mới được gọi là library_loan_return_date phụ thuộc vào my_module. Trong mô-đun này, mở rộng mô hình library.book.loan như sau: class LibraryBookLoan (models.Model): _inherit = 'library.book.loan' expected_return_date = fields.Date ('Due for', required = True) Mở rộng mô hình thư viện.member như sau: class LibraryMember (models.Model): _inherit = 'library.member' loan_duration = fields.Integer ('Thời hạn cho vay', mặc định = 15, required = True) Vì trường expected_return_date là bắt buộc và không có mặc định nào được cung cấp, thuật sĩ ghi lại các khoản vay sẽ ngừng hoạt động vì nó không cung cấp một giá trị cho trường đó khi nó tạo ra các khoản vay.
53
Làm thế nào để làm nó… Để mở rộng logic nghiệp vụ trong mô hình library.loan.wizard, bạn cần thực hiện các bước sau: 1. Trong my_module, sửa đổi phương thức record_loans () trong lớp LibraryLoanWizard. Mã ban đầu là trong trình hướng dẫn Viết để hướng dẫn công thức người dùng trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao. Phiên bản mới là: @ api.multi def record_loans (tự): cho thuật sĩ tự: books = wizard .book_ids loan = self.env ['library.book.loan'] cho cuốn sách trong wizard.book_ids: values = self._prepare_loan (sách) loan.create (giá trị) @ api.multi 54
def _prepare_loan (tự, sách): return {'member_id': self.member_id.id, 'book_id': book.id}
2. Trong thư viện_loan_return_date, tạo một lớp mở rộng library.loan. wizard và định nghĩa phương thức _prepare_loan như sau:
from datetime import timedelta from openerp import models, fields, api class LibraryLoanWizard(models.TransientModel): _inherit = 'library.load.wizard' def _prepare_loan (tự, sách): values = super (LibraryLoanWizard, bản thân) ._ prepar_loan (sách) loan_duration = self.member_id.loan_duration today_str = fields.Date.context_today () today = fields.Date.from_string (today_str) expected = today + timedelta (days = loan_duration) values.update ( {'expected_return_date': 55
fields.Date.to_string (dự kiến)} ) giá trị trả về
Làm thế nào nó hoạt động… Trong bước 1, chúng tôi tái cấu trúc mã từ Trình hướng dẫn viết để hướng dẫn công thức người dùng trong Chương 6, Các kỹ thuật phát triển phía máy chủ nâng cao, sử dụng một mẫu mã hóa rất phổ biến và hữu ích để tạo các bản ghi library.book.loan: việc tạo từ điển của các giá trị được trích xuất trong một phương thức riêng biệt hơn là mã hóa cứng trong phương thức gọi create (). Điều này giúp mở rộng mô-đun addon trong trường hợp các giá trị mới phải được truyền vào thời gian tạo, chính xác là trường hợp chúng ta đang đối mặt. Bước 2 sau đó thực hiện phần mở rộng của logic nghiệp vụ. Chúng tôi định nghĩa một mô hình mở rộng library.loan.wizard và định nghĩa lại phương thức _prepare_loan (). Việc xác định lại bắt đầu bằng cách gọi thực hiện từ lớp cha: 56
values = super (LibraryLoanWizard, self) ._ prepare_loan (sách) Trong trường hợp của các mô hình Odoo, lớp cha không phải là những gì bạn mong đợi bằng cách xem định nghĩa lớp Python. Khung công tác đã tạo động một phân cấp lớp cho bản ghi của chúng ta và lớp cha là định nghĩa của mô hình từ các mô-đun mà chúng ta phụ thuộc. Vì vậy, các cuộc gọi đến siêu () mang lại việc thực hiện library.loan.wizard từ my_module. Trong triển khai này, _prepare_loan () trả về một từ điển các giá trị với 'member_id' và 'book_id'. Chúng tôi cập nhật từ điển này bằng cách thêm khóa 'expected_return_date' trước khi trả lại.
Còn nữa… Trong công thức này, chúng tôi đã chọn để mở rộng hành vi bằng cách gọi thực hiện bình thường và sửa đổi kết quả trả về sau đó. Nó cũng có thể thực hiện một số hành động trước khi gọi thực hiện bình thường, và tất nhiên, chúng ta cũng có thể làm cả hai.
57
Tuy nhiên, những gì chúng ta thấy trong công thức này là khó thay đổi hành vi ở giữa phương thức. Chúng tôi đã phải cấu trúc lại mã để trích xuất một điểm mở rộng thành một phương thức riêng biệt và ghi đè phương thức mới này trong mô-đun mở rộng. Bạn có thể bị cám dỗ để viết lại hoàn toàn một phương thức. Luôn luôn rất thận trọng khi làm như vậy - nếu bạn không gọi phương thức super () của phương thức của bạn, bạn đang phá vỡ cơ chế mở rộng và có khả năng phá vỡ addons mà phần mở rộng của cùng một phương thức sẽ không bao giờ được gọi. Trừ khi bạn đang làm việc trong một môi trường được kiểm soát, trong đó bạn biết chính xác những addons nào đã được cài đặt và bạn đã kiểm tra xem bạn có đang không phá vỡ chúng hay không, tránh làm điều này. Và nếu bạn phải, hãy chắc chắn ghi lại những gì bạn đang làm theo một cách rất rõ ràng.
Những gì bạn có thể làm trước và sau khi gọi thực hiện ban đầu của phương thức? Rất nhiều thứ, bao gồm (nhưng không giới hạn): Sửa đổi các đối số được chuyển đến thực hiện ban đầu (trước đó) Sửa đổi ngữ cảnh được chuyển đến thực hiện ban đầu (trước đây) Sửa đổi kết quả được trả về bởi việc thực hiện ban đầu (sau) Gọi phương thức khác (trước, sau) 58
Tạo bản ghi (trước, sau) Nâng một UserError để hủy bỏ việc thực hiện trong các trường hợp bị cấm (trước, sau) Tách bản thân trong các bản ghi nhỏ hơn và gọi triển khai ban đầu trên mỗi tập con theo cách khác (trước đây)
11. Mở rộng write () và tạo () Việc mở rộng logic nghiệp vụ được định nghĩa trong một công thức Mô hình đã chỉ ra cách mở rộng các phương thức được định nghĩa trên một lớp mô hình. Nếu bạn nghĩ về nó, các phương thức được định nghĩa trên lớp cha của mô hình cũng là một phần của mô hình. Điều này có nghĩa rằng tất cả các phương thức cơ sở được định nghĩa trên mô hình.Model (trên thực tế trên mô hình.BaseModel, là lớp cha của mô hình.Model) cũng có sẵn và có thể được mở rộng. Công thức này cho thấy cách mở rộng create () và write () để kiểm soát truy cập vào một số trường của bản ghi.
59
Sẵn sàng Chúng ta sẽ mở rộng ví dụ thư viện từ mô-đun addon my_module trong Chương 3, Tạo các Mô-đun Odoo. Bạn cũng sẽ cần các nhóm bảo mật được định nghĩa trong Chương 10, Truy cập bảo mật trong Tạo nhóm bảo mật và gán chúng cho công thức Người dùng và quyền truy cập được xác định trong công thức Thêm quyền truy cập bảo mật vào mô hình từ cùng một chương. Sửa đổi tệp bảo mật / ir.model.access.csv để cấp quyền truy cập ghi cho người dùng thư viện để sách:
60
id, tên, model_id: id, group_id: id, perm_read, perm_write, perm_create, perm_unlink access_library_book_user, library.book.user, model_library_book, base.group_user, 1,1,0,0 access_library_book_admin, library.book.admin, model_library_book, base.group_system, 1,0,0,0 Thêm một field_re_re vào mô hình library.book. Chúng tôi chỉ muốn các thành viên của nhóm Quản lý thư viện có thể ghi vào trường đó: class LibraryBook (models.Model): _name = 'library.book' manager_remarks = fields.Text ('Người quản lý nhận xét')
Làm thế nào để làm nó… Để ngăn người dùng không phải là thành viên của nhóm Quản lý thư viện sửa đổi giá trị của manager_remarks, bạn cần thực hiện các bước sau: 1. Mở rộng phương thức create () như sau: @ api.model @ api.returns ('tự', lambda rec: rec.id) 61
def tạo (tự, giá trị): nếu không phải self.user_has_groups ( 'library.group_library_manager'): nếu 'manager_remarks' trong các giá trị: tăng ngoại lệ.UserError ( 'Bạn không được phép sửa đổi' 'manager_remarks') return super (LibraryBook, self) .tạo (giá trị)
2. Mở rộng phương thức write () như sau: @ api.multi def write (self, values): nếu không phải self.user_has_groups ( 'library.group_library_manager'): nếu 'manager_remarks' trong các giá trị: tăng ngoại lệ.UserError ( 'Bạn không được phép sửa đổi' 'manager_remarks' ) trả về siêu (Thư viện, tự) .write (giá trị)
62
3. Mở rộng phương thức fields_get () như sau: @ api.model def fields_get (tự, allfields = Không, write_access = Đúng, thuộc tính = Không): fields = super (LibraryBook, self) .fields_get ( allfields = allfields, write_access = write_access, thuộc tính = thuộc tính ) nếu không phải self.user_has_groups ( 'library.group_library_manager'): nếu 'manager_remarks' trong các trường: các trường ['manager_remarks'] ['readonly'] = True
63
Làm thế nào nó hoạt động… Bước 1 xác định lại phương thức create (). Nó sử dụng một trang trí chúng tôi đã không nhìn thấy cho đến nay, @ api.returns. Trình trang trí này ánh xạ giá trị trả về từ API mới đến cũ API, được dự kiến bởi giao thức RPC. Trong trường hợp này, các cuộc gọi RPC để tạo ra kỳ vọng id cơ sở dữ liệu cho bản ghi mới được 64
tạo, vì vậy chúng tôi chuyển giao trang trí @ api.returns một chức năng ẩn danh, tìm nạp id từ bản ghi mới được thực hiện bởi chúng tôi. Nó là cũng cần thiết nếu bạn muốn mở rộng phương thức copy (). Đừng quên đấy khi mở rộng các phương thức này nếu việc triển khai cơ sở sử dụng API hoặc bạn sẽ gặp khó khăn khi diễn giải thông điệp. Trước khi gọi thực hiện cơ sở create (), phương thức của chúng ta sử dụng user_has_ phương thức groups () để kiểm tra xem người dùng có thuộc về thư viện nhóm hay không. library_manager (đây là ID XML của nhóm). Nếu nó không phải là trường hợp và giá trị là được thông qua cho manager_remarks, một ngoại lệ UserError được nâng lên ngăn cản việc tạo ra kỷ lục. Kiểm tra này được thực hiện trước khi thực hiện cơ sở được gọi. Bước 2 thực hiện điều tương tự cho phương thức write (); trước khi viết, chúng tôi kiểm tra nhóm và sự hiện diện của trường trong các giá trị để viết và tăng UserError nếu có vấn đề. Bước 3 là một phần thưởng nhỏ; phương thức fields_get () được ứng dụng web sử dụng để truy vấn các trường của mô hình và thuộc tính của chúng. Nó trả về một tên từ điển ánh xạ từ điển Python một từ điển thuộc tính trường, chẳng hạn như chuỗi hiển thị hoặc chuỗi trợ giúp. Sở thích gì chúng tôi là thuộc tính chỉ đọc, mà chúng tôi buộc phải True nếu người dùng không phải là người quản lý thư viện. Điều này sẽ làm cho trường chỉ đọc trong ứng dụng web, điều này sẽ tránh cho người dùng không được phép thử để chỉnh sửa nó chỉ để được đối mặt với một thông báo lỗi. Có trường được đặt thành chỉ đọc trong ứng dụng khách web không ngăn RPC các cuộc gọi từ viết nó. Đây là lý do tại sao chúng ta mở rộng create () và write ().
65
Còn nữa… Khi mở rộng write (), lưu ý rằng trước khi gọi super () thực hiện write (), tự vẫn chưa sửa đổi. Bạn có thể sử dụng điều này để so sánh các giá trị hiện tại của các trường với các giá trị trong từ điển giá trị. Trong công thức, chúng tôi đã chọn để đưa ra một ngoại lệ, nhưng chúng tôi cũng có thể loại bỏ trường vi phạm khỏi từ điển giá trị và âm thầm bỏ qua việc cập nhật trường đó trong bản ghi:
66
@ api.multi def write (self, values): nếu không phải self.user_has_groups ( 'library.group_library_manager'): nếu 'manager_remarks' trong các giá trị: del values ['manager_remarks'] trả về siêu (Thư viện, tự) .write (giá trị) Sau khi gọi super (). Write (), nếu bạn muốn thực hiện các hành động bổ sung, bạn phải cảnh giác với bất cứ điều gì có thể gây ra một lời gọi khác để viết (), hoặc bạn sẽ tạo một vòng lặp đệ quy vô hạn. Cách giải quyết là đặt một điểm đánh dấu trong ngữ cảnh để kiểm tra để phá vỡ đệ quy: class MyModel (models.Model): @ api.multi def write (self, values): siêu (MyModel, self) .write (giá trị) nếu self.env.context.get ('MyModelLoopBreaker'): trở về: self = self.with_context (MyModelLoopBreaker = True) self.compute_things () # có thể gây ra các cuộc gọi để ghi
12. Tùy chỉnh cách tìm kiếm hồ sơ Xác định mô hình trình bày và công thức mô hình trong Chương 3, Tạo môđun Odoo đã giới thiệu phương thức name_get (), được sử dụng để tính toán biểu
67
diễn của bản ghi ở nhiều nơi khác nhau, bao gồm trong widget được sử dụng để hiển thị quan hệ Many2one trong ứng dụng web. Công thức này cho thấy cách cho phép tìm kiếm một cuốn sách trong tiện ích Many2one theo tiêu đề, tác giả hoặc ISBN bằng cách xác định lại name_search.
Sẵn sàng Đối với công thức này, chúng tôi sẽ sử dụng định nghĩa mô hình sau: class LibraryBook (models.Model): _name = 'library.book' name = fields.Char ('Tiêu đề') isbn = fields.Char ('ISBN') author_ids = fields.Many2many ('res.partner', 'Tác giả') @ api.model 68
def name_get (tự): result = [] cho cuốn sách tự: authors = book.author_ids.mapped ('name') name = u '% s (% s)'% (book.title, u ',' .join (tác giả)) result.append ((book.id, name)) kết quả trả về Khi sử dụng mô hình này, một cuốn sách trong tiện ích Many2one được hiển thị dưới dạng Tiêu đề Sách (Author1, Author2 ...). Người dùng mong muốn có thể nhập tên tác giả và tìm danh sách được lọc theo tên này, nhưng điều này sẽ không hoạt động khi triển khai mặc định của name_search chỉ sử dụng thuộc tính được tham chiếu bởi thuộc tính _rec_name của lớp mô hình, trong trường hợp của chúng tôi , 'Tên'. Là dịch vụ cho người dùng nâng cao, chúng tôi cũng muốn cho phép lọc theo số ISBN.
Làm thế nào để làm nó…
69
Để cho phép tìm kiếm thư viện.book hoặc theo tên sách, một trong số các tác giả hoặc ISBN, bạn cần xác định phương thức _name_search () trong lớp LibraryBook như sau: @ api.model def _name_search (self, name = '', args = None, toán tử = 'ilike', limit = 100, name_get_uid = None): args = [] nếu args là None args.copy () nếu không (name == '' và toán tử == 'ilike'): args + = ['|', '|', ('tên', toán tử, tên), ('isbn', toán tử, tên), ('author_ids.name', toán tử, tên) ] return super (Thư viện, tự) ._ name_search ( name = '', args = args, toán tử = 'ilike', limit = limit, name_get_uid = name_get_uid)
70
Làm thế nào nó hoạt động… Việc thực thi mặc định của name_search () thực sự chỉ gọi _name_search () phương pháp, thực hiện công việc thực tế. Phương thức _name_search () này có một đối số bổ sung, name_get_uid, được sử dụng trong một số trường hợp góc để tính kết quả bằng sudo (). 71
Chúng tôi chuyển hầu hết các đối số mà chúng tôi nhận được không thay đổi cho việc triển khai siêu () của phương thức: name là một chuỗi chứa giá trị mà người dùng đã nhập cho đến thời điểm này args là Không hoặc miền tìm kiếm được sử dụng làm tiền lọc cho các bản ghi có thể (nó có thể đến từ tham số miền của quan hệ Many2one chẳng hạn) toán tử f là một chuỗi chứa toán tử đối sánh. Nói chung, bạn sẽ có 'ilike' hoặc '=' giới hạn là số hàng tối đa cần truy xuất name_get_uid có thể được sử dụng để chỉ định một người dùng khác khi gọi name_get () trong cuối để tính chuỗi để hiển thị trong tiện ích con Việc triển khai phương pháp của chúng tôi thực hiện như sau: 1. Nó tạo ra một danh sách trống mới nếu args là None, hoặc tạo một bản sao của args nếu không. Chúng tôi tạo một bản sao để tránh những sửa đổi của chúng tôi đối với danh sách có các tác dụng phụ trên người gọi. 2. Sau đó, chúng tôi kiểm tra xem tên không phải là một chuỗi rỗng hoặc nếu nhà điều hành không phải là 'ilike'. Điều này là để tránh tạo ra một tên miền ngu ngốc [('name', ilike, '')] mà không lọc bất kỳ thứ gì. Trong trường hợp đó, chúng ta nhảy thẳng vào cuộc gọi đến siêu () thực hiện. 3. Nếu chúng tôi có tên, hoặc nếu nhà điều hành không phải là 'ilike', thì chúng tôi sẽ thêm một số tính năng lọc tiêu chí để args. Trong trường hợp của chúng ta, chúng ta thêm các mệnh đề sẽ tìm kiếm tên được cung cấp trong tiêu đề của sách, hoặc trong ISBN của họ, hoặc trong tên tác giả. 4. Cuối cùng, chúng ta gọi thực hiện super () với miền được sửa đổi trong args
72
5. và buộc tên thành '' và toán tử không đúng. Chúng tôi làm điều này để buộc mặc định thực hiện _name_search () để không thay đổi miền mà nó nhận được, vì vậy chúng tôi chỉ định sẽ được sử dụng
Còn nữa… Chúng tôi đã đề cập trong phần giới thiệu rằng phương pháp này được sử dụng trong tiện ích Many2one. Để hoàn thành, nó cũng được sử dụng trong các phần sau của Odoo: Đề xuất trong tiện ích tìm kiếm Khi sử dụng toán tử in trên các trường One2many và Many2many trong miền Để tìm kiếm các bản ghi trong tiện ích many2many_tags Để tìm kiếm các bản ghi trong quá trình nhập tệp CSV
73
Xem thêm Xác định công thức mô tả và trình tự mô hình trong Chương 3, Tạo mô-đun Odoo trình bày cách xác định phương thức name_get (), được sử dụng để tạo ra một biểu diễn văn bản của một bản ghi. Bộ lọc Định nghĩa trong danh sách bản ghi: Công thức tên miền trong Chương 8, Chế độ xem phụ trợ cung cấp thêm thông tin về cú pháp tên miền tìm kiếm.
74