บทนำ
Odoo Object-Relational Mapping (ORM) คือหัวใจของการพัฒนา Module บน Odoo ทุกเวอร์ชัน สำหรับ Developer ที่กำลังพัฒนาหรือ Customize ระบบ Odoo 19 การเข้าใจ ORM อย่างลึกซึ้งจะช่วยให้เขียน Code ได้ถูกต้อง มีประสิทธิภาพ และบำรุงรักษาง่ายในระยะยาว บทความนี้จะเจาะลึก ORM ใน Odoo 19 ตั้งแต่โครงสร้าง Model การจัดการ Recordset ไปจนถึง Performance Optimization

โครงสร้าง Model และ Inheritance
ใน Odoo 19 Model แบ่งเป็น 3 ประเภทหลัก ดังนี้
Model (models.Model) สำหรับข้อมูลถาวรที่เก็บในฐานข้อมูล TransientModel (models.TransientModel) สำหรับข้อมูลชั่วคราว เช่น Wizard และ AbstractModel (models.AbstractModel) สำหรับ Base Class ที่ไม่สร้าง Table เอง
Odoo รองรับ 3 รูปแบบ Inheritance ที่ Developer ต้องเข้าใจ
Classical Inheritance ใช้ _name ใหม่พร้อม _inherit เพื่อสร้าง Model ใหม่ที่ extend ของเดิม Extension Inheritance ใช้ _inherit เพียงอย่างเดียวโดยไม่ระบุ _name เพื่อแก้ไข Model เดิมในที่เดิม เหมาะสำหรับการ add field หรือ override method และ Delegation Inheritance ใช้ _inherits เพื่อสร้าง Model ที่ delegate field ไปยัง Model อื่นผ่าน Many2one
Field Types และ Computed Fields
Odoo 19 มี Field Types ครบถ้วนทั้ง Basic Types เช่น Char, Text, Integer, Float, Boolean, Selection, Date, Datetime และ Advanced Types เช่น Binary, Image, Html, Monetary รวมถึง Relational Fields ได้แก่ Many2one, One2many, Many2many
Computed Fields เป็นฟีเจอร์ที่ใช้บ่อยในการ Customize ตัวอย่างการเขียน Computed Field พร้อม store และ inverse:
total_amount = fields.Float(compute='_compute_total', store=True, inverse='_inverse_total')
@api.depends('line_ids.price_unit', 'line_ids.quantity')
def _compute_total(self):
for record in self:
record.total_amount = sum(
line.price_unit * line.quantity
for line in record.line_ids
)
ข้อสำคัญ: ใน Odoo Studio ที่ใช้ safe_eval sandbox ไม่สามารถใช้ self.total_amount = value ได้โดยตรง ต้องใช้ self.update({'total_amount': value}) แทน
Recordset Operations และ Method สำคัญ
Recordset คือชุดของ Record ที่ ORM จัดการ Odoo 19 รองรับ Set Operations ที่หลากหลาย:
recordset_a | recordset_b # Union
recordset_a & recordset_b # Intersection
recordset_a - recordset_b # Difference
CRUD Methods หลักที่ต้องรู้
create(vals_list) สร้าง Record ใหม่ รับ List of Dict ใน Odoo 19 รองรับ Batch Create ที่มีประสิทธิภาพสูง write(vals) อัปเดต Record ใน Recordset ปัจจุบัน unlink() ลบ Record ทั้งหมดใน Recordset search(domain, limit, order) ค้นหา Record ตาม Domain และ search_fetch(domain, field_names) ค้นหาและดึง Field ที่ต้องการในคำสั่งเดียว ช่วยลด Round-trip
Transformation Methods เช่น filtered(), mapped(), sorted(), grouped() ช่วยให้เขียน Code ที่กระชับและอ่านเข้าใจง่าย
Search Domain และ Domain Class
Domain ใช้สำหรับ Filter Record ประกอบด้วย Tuple ของ (field, operator, value) Odoo 19 แนะนำการใช้ Domain Class สำหรับการสร้าง Domain แบบ Dynamic:
from odoo.osv import expression
domain = expression.AND([
[('state', '=', 'sale')],
[('date_order', '>=', '2026-01-01')],
])
รองรับ Relative Date Values เช่น 'today', 'now', '-3d +1H' สำหรับ Domain ที่ต้องการ Dynamic Date Filtering
Command Class สำหรับ One2many และ Many2many
Odoo 19 ใช้ Command Class แทน Integer Tuple แบบเดิม ทำให้ Code อ่านง่ายขึ้นมาก:
from odoo.fields import Command
# สร้าง Line ใหม่
vals = {
'order_line': [
Command.create({'product_id': 1, 'quantity': 5}),
Command.link(existing_line_id),
Command.delete(old_line_id),
]
}
Performance: Cache, Prefetching และ flush
ORM ของ Odoo 19 ใช้ Intelligent Prefetching และ Caching เมื่อ Field หนึ่งถูก access บน Record หนึ่ง ORM จะ prefetch Field เดียวกันบน Record อื่นใน Recordset พร้อมกัน ลด Database Round-trip
เมื่อใช้ Raw SQL ร่วมกับ ORM ต้องระวังการ Sync Cache:
self.env.cr.execute("UPDATE res_partner SET active = false WHERE id = %s", [partner_id])
self.env['res.partner'].invalidate_model(['active']) # หรือ flush_model() ก่อน execute
SQL Class สำหรับ Raw Query ที่ปลอดภัย
Odoo 19 แนะนำการใช้ SQL Wrapper Class สำหรับ Raw Query เพื่อป้องกัน SQL Injection:
from odoo.tools import SQL
query = SQL(
"SELECT id, name FROM %s WHERE state = %s",
SQL.identifier('res_partner'),
'active'
)
self.env.cr.execute(query)
Decorators ที่ Developer ต้องรู้
@api.depends('field1', 'field2') กำหนด Trigger สำหรับ Computed Field
@api.constrains('field1') Validate ค่า Field หลัง write/create
@api.onchange('field1') ทำงานบน Form View เมื่อ Field เปลี่ยน (ไม่บันทึก DB)
@api.model ใช้กับ Method ที่ไม่ต้องการ Recordset เฉพาะ
@api.ondelete(at_uninstall=False) ควบคุมพฤติกรรมเมื่อ Record ถูกลบ
สรุปสำหรับ Developer
ORM ใน Odoo 19 มีการปรับปรุง Performance อย่างมีนัยสำคัญ โดยเฉพาะ Query Planner ที่ลด Round-trip ลงได้ถึง 40% การเข้าใจ Prefetch, Cache Management, Command Class และ SQL Wrapper จะช่วยให้ Module ที่พัฒนามีประสิทธิภาพสูงและปลอดภัยจาก SQL Injection ผู้ที่ต้องการเรียนรู้เพิ่มเติมควรศึกษาจาก Official Documentation และทดลองเขียน Module จริงใน Development Environment
Reference Links:
- Odoo 19 ORM API Reference: https://www.odoo.com/documentation/19.0/developer/reference/backend/orm.html
- Odoo 19 Developer Documentation: https://www.odoo.com/documentation/19.0/developer.html
- Building a Module Tutorial: https://www.odoo.com/documentation/19.0/developer/tutorials/backend.html
- Odoo 19 Technical Guide: https://highshine.in/odoo-19-technical-developer-guide
#Odoo #OdooDeveloper #ORM #Odoo19 #Python #ERP #OdooCustomization #ModuleDevelopment