Ở bài recordset ta nói mỗi recordset "mang theo env". Giờ mở hộp đó ra: env là môi trường ORM — nó trả lời câu hỏi "ai đang thao tác, dưới công ty nào, ngôn ngữ gì, trong giao dịch nào". Hiểu env là hiểu bối cảnh mọi lời gọi ORM. Mở màn chương "môi trường & ngữ cảnh", bài này mổ xẻ từng thành phần của env.

env gồm những gì?

env môi trường ORM mọi recordset đều mang theo trong Odoo 19: env quyết định làm việc dưới danh nghĩa ai công ty nào ngôn ngữ gì giao dịch nào; bảng thành phần, env.cr là con trỏ cơ sở dữ liệu Cursor dùng để chạy SQL thô quản giao dịch commit rollback, env.uid là id người dùng hiện tại dùng để biết ai đang thao tác phân quyền, env.user là recordset res.users của uid dùng để đọc thông tin người dùng, env.context là dict ngữ cảnh lang tz dùng để truyền tuỳ chọn ngôn ngữ múi giờ cờ tuỳ biến, env.company là công ty hiện tại dùng để lọc dữ liệu đa công ty, env.su là có đang superuser dùng để bỏ qua phân quyền cẩn thận; từ recordset lấy env gọi tiếp model khác, partner.env sale.order search, partner.env.uid cùng env với recordset gốc, self.env.ref base.group_user lấy bản ghi theo XML id

Hình 1: env là một object Environment gói sáu thứ chính. env.cr (con trỏ CSDL — chạy SQL thô, quản giao dịch), env.uid/env.user (người dùng hiện tại — nền của phân quyền), env.context (ngôn ngữ, múi giờ, cờ tuỳ biến), env.company (công ty hiện tại — lọc đa công ty), env.su (có đang superuser không). Mọi recordset đều gắn với một env.

Nhìn giá trị thật

Đây là giá trị env thật trong odoo shell trên db blog19:

env chạy thật trong odoo shell database blog19: type env name là Environment; env.uid là 1; env.user.name là OdooBot; type env.cr name là Cursor con trỏ cơ sở dữ liệu; env.company.name là Công ty TNHH Cà Phê Việt; env.context là dict lang en_US tz False uid 1; env.su là True đang superuser trong shell; recordset mang theo env cùng một môi trường, rs bằng P.search list limit 1, rs.env is env trả True, rs.env res.users search_count list trả 4 gọi model khác qua env; env.cr chạy SQL thô trực tiếp, env.cr.execute SELECT count sao FROM res_partner rồi env.cr.fetchone trả 33

Hình 2: Giá trị thật. env.uid=1, env.user.name='OdooBot' (shell chạy dưới superuser). env.cr là một Cursor. env.company là "Công ty TNHH Cà Phê Việt". env.context = {'lang':'en_US', 'tz':False, 'uid':1}. Và điểm mấu chốt: recordset rs có rs.env is env = True — nó dùng đúng cùng môi trường. Từ đó gọi model khác (rs.env['res.users']), và env.cr chạy SQL thô trả count=33.

Từng thành phần dùng khi nào

env.cr — con trỏ CSDL

Cho các thao tác sát CSDL: chạy SQL thô (khi ORM không đủ), quản giao dịch. Trong odoo shell bạn gọi env.cr.commit() để lưu.

env.cr.execute("SELECT id FROM res_partner WHERE active = %s", (True,))
rows = env.cr.fetchall()   # dùng %s tham số hoá — KHÔNG nối chuỗi (chống SQL injection)

Cảnh báo: SQL thô bỏ qua ORM (không chạy computed, constrains, phân quyền) — chỉ dùng khi thật cần, và luôn tham số hoá.

env.uid / env.user — người dùng

Nền của phân quyền (record rules, access rights). Odoo dùng env.uid để quyết định bạn thấy/sửa được bản ghi nào.

if self.env.user.has_group('base.group_system'):
    ...  # chỉ admin

env.context — ngữ cảnh

Dict truyền tuỳ chọn không thuộc dữ liệu: lang (ngôn ngữ dịch), tz (múi giờ hiển thị), và cờ tuỳ biến để đổi hành vi. Đổi context là chủ đề bài sau.

env.company — công ty hiện tại

Trong hệ đa công ty, quyết định dữ liệu của công ty nào được thấy. env.companies là tập các công ty đang bật.

env đi cùng recordset

Điểm quan trọng nhất về mặt kỹ thuật: recordset gắn chặt với env. Hai recordset cùng model, cùng id, nhưng khác env (khác user, khác company, khác context) là hai thứ khác nhau — cho kết quả khác nhau (phân quyền, ngôn ngữ, computed theo ngữ cảnh).

Từ bất kỳ recordset nào, rec.env cho bạn môi trường đó, và rec.env['model.khac'] gọi sang model khác giữ nguyên ngữ cảnh. Đây là lý do trong method bạn luôn viết self.env[...] chứ không import model trực tiếp.

Vài truy cập hữu ích

self.env.ref('base.group_user')     # lấy bản ghi theo XML id
self.env.company                     # công ty hiện tại
self.env.user                        # người dùng hiện tại
self.env['ir.config_parameter'].sudo().get_param('...')  # đọc cấu hình
self.env.su                          # có đang superuser?

Nhớ ba ý

  1. env là môi trường ORM gói cr (con trỏ CSDL), uid/user (người dùng), context (lang/tz/cờ), company (công ty), su (superuser) — mọi recordset mang theo.
  2. Đo thật: rs.env is env = True — recordset dùng đúng env; từ đó rec.env['model'] gọi model khác giữ ngữ cảnh; env.cr chạy SQL thô (nhớ tham số hoá).
  3. Recordset cùng id nhưng khác env là khác nhau (phân quyền/ngôn ngữ/computed theo ngữ cảnh) — luôn dùng self.env[...] trong code.

env quan trọng nhất ở chỗ đổi được — chạy dưới ngữ cảnh khác. Phần sau đi vào with_context — cách tạo một recordset mới với context thay đổi để đổi ngôn ngữ, múi giờ, hay truyền cờ điều khiển hành vi.