السياق والنطاق في أودو — مرجع المطوّر (أودو 16–19)
دليل عملي لنطاقات أودو (تصفية السجلات) والسياق (تمرير الحالة كأزواج مفتاح-قيمة) — كيف تعمل وأين تظهر وقواعد الصيغة من أودو 16 إلى أودو 19. موثق لأودو 18 و19.
يظهر مفهومان في جميع أنحاء عروض XML لأودو ونماذج Python وطلبات RPC وكود JavaScript: **النطاق** و**السياق**. النطاق هو قائمة من الشروط تُستخدم لتصفية السجلات — ما يعادل جملة SQL `WHERE` في أودو. السياق هو قاموس Python يحمل الحالة بين واجهة المستخدم والخادم، ويتحكم في القيم الافتراضية وتبديل العروض والتجميع وأعلام السلوك. إتقان كليهما ضروري لبناء عروض وإجراءات وأساليب نماذج موثوقة.
الجزء 1 — صيغة النطاق
النطاق هو قائمة Python من الثلاثيات (tuples)، حيث تأخذ كل ثلاثية الشكل `(field_name, operator, value)`. تُدمج الثلاثيات المتعددة بمنطق `AND` افتراضيًا. تُغيّر مشغّلات البادئة `'|'` (OR) و`'!'` (NOT) المنطق للشروط التالية مباشرةً.
# Basic domain — AND logic (default)
# Returns records where state is 'sale' AND partner_id is 7
domain = [('state', '=', 'sale'), ('partner_id', '=', 7)]
# OR logic — use '|' prefix before the two conditions it joins
# Returns records where state is 'sale' OR state is 'done'
domain = ['|', ('state', '=', 'sale'), ('state', '=', 'done')]
# NOT logic — use '!' prefix before the condition to negate
# Returns records where state is NOT 'cancel'
domain = ['!', ('state', '=', 'cancel')]
# Empty domain — matches all records
domain = []مرجع مشغّلات النطاق
| المشغّل | المعنى | نوع القيمة | مثال |
|---|---|---|---|
| = | يساوي | قيمة مفردة | ('state', '=', 'done') |
| != | لا يساوي | قيمة مفردة | ('state', '!=', 'cancel') |
| > | أكبر من | رقم / تاريخ | ('amount_total', '>', 0) |
| >= | أكبر من أو يساوي | رقم / تاريخ | ('date', '>=', '2026-01-01') |
| < | أصغر من | رقم / تاريخ | ('qty', '<', 10) |
| <= | أصغر من أو يساوي | رقم / تاريخ | ('date', '<=', '2026-12-31') |
| =? | يساوي أو الحقل False/None (تساوي آمن للقيم الفارغة) | قيمة مفردة أو False | ('partner_id', '=?', False) |
| like | يحتوي على سلسلة فرعية (حساس لحالة الأحرف) | سلسلة نصية | ('name', 'like', 'Odoo') |
| ilike | يحتوي على سلسلة فرعية (غير حساس لحالة الأحرف) | سلسلة نصية | ('name', 'ilike', 'odoo') |
| =like | يطابق النمط مع حرف بدل % (حساس لحالة الأحرف) | سلسلة نصية مع % | ('ref', '=like', 'INV%') |
| =ilike | يطابق النمط مع حرف بدل % (غير حساس لحالة الأحرف) | سلسلة نصية مع % | ('ref', '=ilike', 'inv%') |
| in | القيمة موجودة في القائمة | قائمة | ('state', 'in', ['sale', 'done']) |
| not in | القيمة غير موجودة في القائمة | قائمة | ('state', 'not in', ['draft', 'cancel']) |
| child_of | السجل أو أبناؤه (النماذج الهرمية) | معرّف أو قائمة معرّفات | ('category_id', 'child_of', 5) |
| parent_of | السجل أو آباؤه (النماذج الهرمية) | معرّف أو قائمة معرّفات | ('category_id', 'parent_of', 12) |
النطاق في عروض XML
في عروض XML، تُكتب النطاقات كسلاسل نصية (يُعبَّر عن قائمة Python كسلسلة حرفية). يُقيَّم السياق النطاق وفق قيم حقول السجل الحالي. استخدم `uid` لمعرّف المستخدم الحالي و`context_today()` لتاريخ اليوم.
<!-- Domain on a Many2one field — filters dropdown options -->
<field name="user_id"
domain="[('groups_id', 'in', [ref('base.group_user')])]"/>
<!-- Domain on an action button — filters records opened by the action -->
<button name="action_view_invoices" type="object"
domain="[('state', '=', 'posted'), ('partner_id', '=', active_id)]"/>
<!-- Domain referencing current user -->
<field name="assigned_to"
domain="[('share', '=', False), ('id', '!=', uid)]"/>
<!-- Domain using context_today() for date comparison -->
<filter name="overdue"
domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"/>النطاق في Python (ORM search وsearch_count)
# search() — returns recordset matching domain
invoices = self.env['account.move'].search([
('state', '=', 'posted'),
('partner_id', '=', self.partner_id.id),
('invoice_date', '>=', '2026-01-01'),
], order='invoice_date desc', limit=10)
# search_count() — returns integer count
count = self.env['sale.order'].search_count([
('user_id', '=', self.env.uid),
('state', 'not in', ['cancel', 'draft']),
])
# filtered() — applies a domain-like condition to an existing recordset
# (uses a lambda, not a domain list — different API)
confirmed = orders.filtered(lambda o: o.state == 'sale')
# _search() / _domain — advanced: used inside compute methods
# to build dynamic domains programmatically
domain = [('company_id', '=', self.env.company.id)]
if self.partner_id:
domain += [('partner_id', '=', self.partner_id.id)]الجزء 2 — السياق
السياق هو قاموس Python (`dict`) يُمرَّر مع كل طلب RPC وفتح إجراء واستدعاء مُعالج. يحمل معلومات جلسة المستخدم (اللغة والمنطقة الزمنية والسجل النشط) والأعلام التي يضبطها المطوّر والتي تعدّل سلوك النماذج والعروض والإجراءات. يمكنك قراءته في Python عبر `self.env.context` وتمديده باستخدام `self.with_context(...)`.
مفاتيح السياق القياسية
| المفتاح | النوع | يُضبط من قِبل | التأثير |
|---|---|---|---|
| lang | str | ملف تعريف المستخدم | رمز اللغة النشطة — مثلًا `'en_US'` أو `'ar_001'`. يُستخدم للترجمات. |
| tz | str | ملف تعريف المستخدم | سلسلة المنطقة الزمنية — مثلًا `'Asia/Riyadh'`. يُستخدم بواسطة حقول التاريخ والوقت. |
| uid | int | الجلسة | معرّف المستخدم الحالي. متاح في تعبيرات النطاق في عروض XML. |
| active_id | int | واجهة المستخدم / الإجراء | معرّف السجل المحدد/النشط حاليًا. يُمرَّر عند فتح مُعالج أو إجراء مرتبط. |
| active_ids | list[int] | واجهة المستخدم / الإجراء | قائمة معرّفات جميع السجلات المحددة (إجراءات متعددة السجلات). |
| active_model | str | واجهة المستخدم / الإجراء | الاسم التقني للنموذج الذي أُطلق منه الإجراء — مثلًا `'sale.order'`. |
| default_field_name | any | المطوّر / الإجراء | يضبط قيمة افتراضية لـ `field_name` عند إنشاء سجل جديد في العرض المفتوح. استبدل `field_name` باسم الحقل الفعلي. |
| no_create | bool | المطوّر | عند True على حقل Many2one، يُعطّل خيار الإنشاء السريع في القائمة المنسدلة. |
| no_open | bool | المطوّر | عند True على حقل Many2one، يُعطّل خيار الرابط الخارجي / فتح السجل. |
| group_by | list[str] | المطوّر / مرشح البحث | قائمة أسماء الحقول لتجميع السجلات بها في عروض القائمة/Kanban. |
| search_default_field_name | 1 | سياق الإجراء | يُطبّق مسبقًا مرشح بحث لـ `field_name` عند فتح الإجراء. يجب أن تكون القيمة `1` (قيمة صحيحة). |
السياق في عروض XML والإجراءات
<!-- Set default values when opening a new record form -->
<field name="partner_id"
context="{'default_company_type': 'company', 'default_country_id': ref('base.sa')}"/>
<!-- Pre-apply a search filter when an action opens -->
<record id="action_sale_order_my_orders" model="ir.actions.act_window">
<field name="name">My Sales Orders</field>
<field name="res_model">sale.order</field>
<field name="context">{
'search_default_user_id': uid,
'search_default_state_sale': 1
}</field>
</record>
<!-- Group by partner when the action opens -->
<record id="action_invoices_grouped" model="ir.actions.act_window">
<field name="name">Invoices by Partner</field>
<field name="res_model">account.move</field>
<field name="context">{'group_by': ['partner_id']}</field>
</record>
<!-- Disable quick-create on a Many2one field -->
<field name="product_id"
context="{'no_create': True, 'no_open': True}"/>السياق في أساليب نماذج Python
# Read context in a model method
def action_confirm(self):
# Check if called from a specific flow
if self.env.context.get('from_purchase'):
# custom behaviour
pass
# with_context() — extend context for a specific ORM call
# Does NOT modify self.env.context — returns a new recordset
records_ar = self.env['product.template'].with_context(lang='ar_001').search([])
# Common: set a flag to skip a compute or onchange in a loop
self.with_context(skip_onchange=True).write({'state': 'done'})
# Pass context when opening a wizard action programmatically
return {
'type': 'ir.actions.act_window',
'res_model': 'my.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_partner_id': self.partner_id.id,
'default_amount': self.amount_total,
'active_id': self.id,
'active_model': self._name,
},
}استخدام النطاق والسياق معًا
يمكن أن تحمل الحقول والأزرار والإجراءات `domain` و`context` في آنٍ واحد. يُصفّي النطاق السجلات المعروضة أو القابلة للتحديد؛ بينما يتحكم السياق في القيم الافتراضية والأعلام للسجلات المنشأة أو المفتوحة في العرض الناتج.
<!-- Smart button — open related records filtered by domain,
with a default set via context for any new record created there -->
<button name="action_view_picking"
type="object"
domain="[('sale_id', '=', active_id), ('state', '!=', 'cancel')]"
context="{
'default_sale_id': active_id,
'default_partner_id': partner_id,
'search_default_state_ready': 1
}"
string="Transfers"/>ملاحظات الإصدار
| إصدار أودو | التغييرات الرئيسية في النطاق / السياق |
|---|---|
| أودو 16 | صيغة النطاق لم تتغير. إهمال `attrs` (راجع دليل D6 المرافق). إعادة تسمية `<tree>` → `<list>`. `context_today()` متاح في نطاقات XML. |
| أودو 17 | حذف `attrs` (النطاق على الحقول غير متأثر). إضافة مشغّل `parent_of` للنماذج الهرمية. نشر السياق في الإجراءات لم يتغير. |
| أودو 18 | لا توجد تغييرات كاسرة لواجهة API للنطاق أو السياق. سلوك مشغّل `=?` الآمن للقيم الفارغة موثق في سجل تغييرات ORM. |
| أودو 19 | لا توجد تغييرات كاسرة. سلوك النطاق والسياق مستقر. تحسينات أداء ORM لـ `search()` لمجموعات البيانات الكبيرة. |
تحتاج مساعدة في تطوير أودو المخصص أو ترقية الوحدات؟
يتولى مطورونا المعتمدون من أودو منطق النطاق المخصص وسير العمل المدفوع بالسياق وترحيل الوحدات من أودو 15 إلى 17/18/19 — مع التحقق الكامل من الامتثال لـ ZATCA وGOSI.
الأسئلة الشائعة
ما الفرق بين النطاق على حقل والنطاق على إجراء؟
هل يمكنني استخدام قيمة حقل محسوب داخل نطاق؟
كيف أُمرّر السياق من إجراء Python إلى مُعالج؟
ماذا يفعل with_context() وهل يُعدّل البيئة الحالية؟
كيف أُطبّق مرشح بحث مسبقًا عند فتح إجراء؟
هل يمكنني استخدام مفاتيح السياق لتعطيل @api.onchange في استدعاء write()؟

iWesabe Editorial Team
رؤى عملية حول Odoo ERP وامتثال ZATCA والعمليات الرقمية للشركات السعودية — بقلم فرق الاستشارات والمالية والهندسة في iWesabe.
مقالات ذات صلة
إهمال attrs في أودو: الانتقال إلى invisible وreadonly وrequired في أودو 16–19
تم إهمال قاموس attrs في أودو 16 وحذفه في أودو 17. يحدد هذا الدليل كل نمط attrs ومقابله من السمات المباشرة — مع كود قبل/بعد لحالات الاستخدام الأكثر شيوعًا. موثق لأودو 18 و19.
كيفية إضافة الألوان إلى صفوف وخلايا عرض القائمة في أودو
استخدم سمات decoration-* لترميز الصفوف والخلايا بالألوان في عروض قائمة أودو بناءً على قيم الحقول — مع صيغة التعبيرات وجميع أنواع الزخرفة وملاحظة الترحيل لإعادة التسمية من tree إلى list. موثق لأودو 18 و19.