Làm chủ Odoo ORM, API và xử lý tác vụ nặng cho hệ thống lớn

Khi một hệ thống Odoo bắt đầu mở rộng, các vấn đề thường không đến từ việc thiếu tính năng, mà đến từ:

  • Màn hình ngày càng chậm khi dữ liệu tăng

  • Cron chạy lâu, lock database, user thao tác bị treo

  • Compute field làm CPU tăng đột biến

  • Tích hợp API bên ngoài lúc chạy được, lúc lỗi, rất khó debug

  • Import / sync dữ liệu lớn bị timeout hoặc crash worker

Bài viết này tập trung vào kỹ thuật lập trình nâng cao trong Odoo, giúp bạn viết code:

  • Chạy nhanh

  • Ổn định trong production

  • Dễ mở rộng khi dữ liệu và số user tăng

1. Odoo ORM nâng cao: Viết code ORM để hệ thống có thể scale

Odoo ORM rất mạnh, nhưng phần lớn vấn đề hiệu năng trong Odoo đến từ việc dùng ORM sai cách.

Tư duy cốt lõi:

ORM không chậm, nhưng ORM bị lạm dụng thì sẽ chậm

1.1 Recordset không phải là object đơn lẻ

Trong Odoo, self hầu như luôn là một recordset (tập record), kể cả khi chỉ có 1 record.

❌ Cách viết dễ gây chậm

for order in orders:

    print(order.partner_id.name)

 

Nhìn qua thì đơn giản, nhưng rất dễ phát sinh N+1 query.

✅ Cách viết tối ưu hơn

partner_names = orders.mapped(‘partner_id.name’)

 

Nguyên tắc quan trọng:

  • Tránh loop record nếu có thể xử lý theo batch

  • Ưu tiên mapped, read, read_group

1.2 N+1 Query Problem – lỗi kinh điển trong ORM

Giả sử bạn có 5.000 đơn bán và muốn in ra tên sản phẩm:

for order in orders:

    for line in order.order_line:

        print(line.product_id.name)

 

Rất dễ xảy ra tình huống:

  • 1 query lấy orders

  • N query lấy order_line

  • N query lấy product

 

 

 

Cách xử lý tốt hơn

lines = orders.mapped(‘order_line’)

product_names = lines.mapped(‘product_id.name’)

 

ORM có thể prefetch và gom query, giảm đáng kể số lần truy cập database.

1.3 Chọn đúng ORM API theo mục đích

Nhu cầu API nên dùng
Lấy data đơn giản search_read()
Thống kê / dashboard read_group()
Logic nghiệp vụ search() + recordset
Trích xuất field mapped()
Lọc nhẹ filtered()

Ví dụ: Dashboard doanh thu

❌ Cộng bằng Python (chậm):

total = sum(self.env[‘sale.order’].search([]).mapped(‘amount_total’))

 

✅ Đẩy xuống database:

res = self.env[‘sale.order’].read_group(

    [],

    [‘amount_total:sum’],

    []

)

total = res[0][‘amount_total_sum’]

 

👉 Database làm tốt việc thống kê hơn Python rất nhiều.

1.4 Compute field – “sát thủ thầm lặng” của hiệu năng

Ví dụ sai phổ biến

@api.depends(‘partner_id’)

def _compute_sale_count(self):

    for rec in self:

        rec.sale_count = self.env[‘sale.order’].search_count([

            (‘partner_id’, ‘=’, rec.partner_id.id)

        ])

 

Nếu có 1000 record → 1000 query.

Cách tối ưu bằng read_group

@api.depends(‘partner_id’)

def _compute_sale_count(self):

    partners = self.mapped(‘partner_id’)

    data = self.env[‘sale.order’].read_group(

        [(‘partner_id’, ‘in’, partners.ids)],

        [‘partner_id’],

        [‘partner_id’],

        lazy=False,

    )

    count_map = {d[‘partner_id’][0]: d[‘partner_id_count’] for d in data}

    for rec in self:

        rec.sale_count = count_map.get(rec.partner_id.id, 0)

Nguyên tắc compute field chuẩn production

  • Không query trong loop

  • Luôn batch

  • Giảm số field trong @api.depends

  • Compute nặng → cân nhắc store=True hoặc cron

1.5 Khi nào nên dùng SQL thuần?

SQL thuần không xấu, nhưng chỉ nên dùng khi:

  • Query cực nặng

  • ORM tạo query không tối ưu

  • Bạn hiểu rõ nghiệp vụ

self.env.cr.execute(“””

    UPDATE sale_order

       SET x_synced = TRUE

     WHERE state = ‘sale’

“””)

 

⚠️ Lưu ý:

  • SQL bỏ qua compute, constraint Python

  • Phải tự đảm bảo dữ liệu đúng

2. Lập trình Odoo API nâng cao & tích hợp hệ thống ngoài

Phần lớn hệ thống Odoo “chết dần” vì tích hợp viết sai kiến trúc.

2.1 Kiến trúc module tích hợp nên có

Cấu trúc gợi ý cho production:

integration_module/

├─ models/

├─ services/

├─ controllers/

├─ data/

 

  • models: mapping dữ liệu

  • services: xử lý logic & gọi API

  • controllers: webhook / endpoint

  • cron: xử lý async

2.2 Tuyệt đối không gọi API ngoài trong create() / write()

Vì sao rất nguy hiểm?

  • User bị chờ lâu

  • API lỗi → rollback toàn bộ transaction

  • Dễ lock DB

Pattern đúng

  1. create/write chỉ lưu dữ liệu

  2. Tạo job

  3. Cron / worker xử lý

def write(self, vals):

    res = super().write(vals)

    self.env[‘integration.job’].create({

        ‘model’: self._name,

        ‘res_id’: self.id,

        ‘state’: ‘pending’

    })

    return res

 

2.3 Thiết kế HTTP client đúng chuẩn production

Một HTTP client tốt cần:

  • timeout rõ ràng

  • retry có kiểm soát

  • log dễ debug

  • tránh duplicate (idempotency)

response = requests.post(

    url,

    json=payload,

    timeout=10

)

response.raise_for_status()

 

2.4 Webhook: nhanh – an toàn – async

Nguyên tắc:

  • verify chữ ký

  • tạo job

  • trả 200 OK ngay

Không xử lý nặng trong controller.

3. Multi-threading & Multi-processing trong Odoo

Sự thật quan trọng:

Odoo ORM không thread-safe

3.1 Odoo xử lý song song như thế nào?

  • Nhiều worker process

  • Mỗi request = 1 process

  • Không phải multi-thread

3.2 Vì sao không nên dùng Python threading với ORM?

  • Cursor bị chia sẻ sai

  • Cache ORM bị hỏng

  • Lỗi ngẫu nhiên, rất khó debug

👉 Tránh threading nếu có ORM.

3.3 Cách đúng để xử lý tác vụ dài

Cách 1: Job queue / cron (khuyến nghị)

Flow:

User Action

   ↓

Create Job

   ↓

Cron / Worker

   ↓

Update Result

 

Cách 2: Chia batch

batch = self.search([(‘done’, ‘=’, False)], limit=2000)

batch.process()

batch.write({‘done’: True})

 

Cách 3: External worker (Celery, RQ)

Dùng khi:

  • tác vụ nặng CPU

  • dữ liệu cực lớn

  • cần scale độc lập

4. Checklist ORM & API trước khi lên production

  • Không query trong loop

  • Compute field đã batch

  • Dashboard dùng read_group

  • Không gọi API ngoài trong request user

  • Task dài chạy async

  • Batch size có giới hạn

  • Có log & theo dõi job

Kết luận

Lập trình Odoo nâng cao không phải viết code phức tạp, mà là:

  • Hiểu ORM hoạt động ra sao

  • Biết chỗ nào cần database xử lý

  • Thiết kế tích hợp bền vững

  • Tôn trọng mô hình concurrency của Odoo

Nếu bạn làm chủ các kỹ thuật trong bài này, hệ thống Odoo của bạn sẽ:

  • chạy ổn định khi dữ liệu tăng

  • ít bug production

  • dễ bảo trì và mở rộng lâu dài
Guest