تطوير أودو

السياق والنطاق في أودو — مرجع المطوّر (أودو 16–19)

دليل عملي لنطاقات أودو (تصفية السجلات) والسياق (تمرير الحالة كأزواج مفتاح-قيمة) — كيف تعمل وأين تظهر وقواعد الصيغة من أودو 16 إلى أودو 19. موثق لأودو 18 و19.

iWesabe Editorial Team١ مارس ٢٠٢٢6 دقائق للقراءة

يظهر مفهومان في جميع أنحاء عروض XML لأودو ونماذج Python وطلبات RPC وكود JavaScript: **النطاق** و**السياق**. النطاق هو قائمة من الشروط تُستخدم لتصفية السجلات — ما يعادل جملة SQL `WHERE` في أودو. السياق هو قاموس Python يحمل الحالة بين واجهة المستخدم والخادم، ويتحكم في القيم الافتراضية وتبديل العروض والتجميع وأعلام السلوك. إتقان كليهما ضروري لبناء عروض وإجراءات وأساليب نماذج موثوقة.

الجزء 1 — صيغة النطاق

النطاق هو قائمة Python من الثلاثيات (tuples)، حيث تأخذ كل ثلاثية الشكل `(field_name, operator, value)`. تُدمج الثلاثيات المتعددة بمنطق `AND` افتراضيًا. تُغيّر مشغّلات البادئة `'|'` (OR) و`'!'` (NOT) المنطق للشروط التالية مباشرةً.

python
# 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 = []

مرجع مشغّلات النطاق

مشغّلات نطاق أودو — موثقة لأودو 16–19
المشغّلالمعنىنوع القيمةمثال
=يساويقيمة مفردة('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()` لتاريخ اليوم.

xml
<!-- 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', '&lt;', context_today().strftime('%Y-%m-%d'))]"/>

النطاق في Python (ORM search وsearch_count)

python
# 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(...)`.

مفاتيح السياق القياسية

مفاتيح السياق الشائعة الاستخدام في أودو 16–19
المفتاحالنوعيُضبط من قِبلالتأثير
langstrملف تعريف المستخدمرمز اللغة النشطة — مثلًا `'en_US'` أو `'ar_001'`. يُستخدم للترجمات.
tzstrملف تعريف المستخدمسلسلة المنطقة الزمنية — مثلًا `'Asia/Riyadh'`. يُستخدم بواسطة حقول التاريخ والوقت.
uidintالجلسةمعرّف المستخدم الحالي. متاح في تعبيرات النطاق في عروض XML.
active_idintواجهة المستخدم / الإجراءمعرّف السجل المحدد/النشط حاليًا. يُمرَّر عند فتح مُعالج أو إجراء مرتبط.
active_idslist[int]واجهة المستخدم / الإجراءقائمة معرّفات جميع السجلات المحددة (إجراءات متعددة السجلات).
active_modelstrواجهة المستخدم / الإجراءالاسم التقني للنموذج الذي أُطلق منه الإجراء — مثلًا `'sale.order'`.
default_field_nameanyالمطوّر / الإجراءيضبط قيمة افتراضية لـ `field_name` عند إنشاء سجل جديد في العرض المفتوح. استبدل `field_name` باسم الحقل الفعلي.
no_createboolالمطوّرعند True على حقل Many2one، يُعطّل خيار الإنشاء السريع في القائمة المنسدلة.
no_openboolالمطوّرعند True على حقل Many2one، يُعطّل خيار الرابط الخارجي / فتح السجل.
group_bylist[str]المطوّر / مرشح البحثقائمة أسماء الحقول لتجميع السجلات بها في عروض القائمة/Kanban.
search_default_field_name1سياق الإجراءيُطبّق مسبقًا مرشح بحث لـ `field_name` عند فتح الإجراء. يجب أن تكون القيمة `1` (قيمة صحيحة).

السياق في عروض XML والإجراءات

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

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` في آنٍ واحد. يُصفّي النطاق السجلات المعروضة أو القابلة للتحديد؛ بينما يتحكم السياق في القيم الافتراضية والأعلام للسجلات المنشأة أو المفتوحة في العرض الناتج.

xml
<!-- 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.

واتساب

الأسئلة الشائعة

ما الفرق بين النطاق على حقل والنطاق على إجراء؟
النطاق على حقل علائقي (سمة `domain` على Many2one أو Many2many أو One2many) يُصفّي السجلات المعروضة في القائمة المنسدلة أو القائمة عند اختيار المستخدم لسجل مرتبط — يتحكم في ما هو *قابل للتحديد*. النطاق على `ir.actions.act_window` يُصفّي السجلات المحملة عند فتح الإجراء لعرض قائمة أو نموذج — يتحكم في ما هو *معروض*. كلاهما يستخدم صيغة قائمة النطاق ذاتها.
هل يمكنني استخدام قيمة حقل محسوب داخل نطاق؟
نعم، في طلبات ORM لـ Python (`search` و`search_count`) يمكن استخدام أي حقل محسوب مخزَّن أو غير مخزَّن في نطاق، طالما كان مُعلَّمًا بـ `store=True`. بالنسبة للحقول المحسوبة غير المخزَّنة، سيجلب ORM قيمة الحقل لكل سجل ويُصفّي في Python — هذا يعمل لكنه أبطأ من الحقل المخزَّن المدعوم بـ SQL. في سلاسل نطاق عرض XML (لقوائم المنسدلة Many2one أو نطاقات الإجراءات)، يمكنك الإشارة إلى الحقل المحسوب بالاسم طالما كان مخزَّنًا.
كيف أُمرّر السياق من إجراء Python إلى مُعالج؟
أعد قاموس إجراء من طريقة نموذجك وأدرج مفتاح `context`. يدمج أودو هذا السياق مع سياق الجلسة الحالي قبل فتح عرض المُعالج. استخدم مفاتيح `default_field_name` لملء حقول المُعالج مسبقًا، و`active_id` / `active_ids` لإخبار المُعالج بالسجلات التي استُدعي منها. مثال: `return {'type': 'ir.actions.act_window', 'res_model': 'my.wizard', 'view_mode': 'form', 'target': 'new', 'context': {'default_partner_id': self.partner_id.id, 'active_id': self.id}}`.
ماذا يفعل with_context() وهل يُعدّل البيئة الحالية؟
يُعيد `with_context()` مجموعة سجلات جديدة (والبيئة المرتبطة بها) مع دمج المفاتيح المعطاة في السياق. لا يُعدّل `self.env.context` — مجموعة السجلات الأصلية لم تتغير. هذا يعني أن `self.with_context(lang='ar_001').search([])` يقرأ الترجمات بالعربية دون التأثير على أي كود آخر يعمل في نفس المعاملة. استخدم دائمًا `self.with_context(...)` على مجموعة السجلات التي تريد تطبيق السياق عليها، وخصّص النتيجة إذا كنت بحاجة إليها لاحقًا.
كيف أُطبّق مرشح بحث مسبقًا عند فتح إجراء؟
أضف `search_default_: 1` إلى `context` الإجراء، حيث `` هو سمة `name` لعنصر `` في عرض البحث. يجب أن تكون القيمة `1` (عدد صحيح، قيمة صحيحة). مثال: إذا كان لديك `` في عرض البحث، اضبط `context="{'search_default_my_orders': 1}"` على الإجراء ليُطبَّق مسبقًا عند فتح القائمة.
هل يمكنني استخدام مفاتيح السياق لتعطيل @api.onchange في استدعاء write()؟
`@api.onchange` هو حدث من جانب العميل يُطلقه متصفح واجهة المستخدم — لا يُطلَق أثناء استدعاءات `write()` من جانب الخادم. لذلك لا شيء لتعطيله. ما يريد المطورون أحيانًا تخطيه هو حساب `@api.depends` أو فحص `@api.constrains`. بالنسبة للحساب، علّمه بـ `store=False` إذا أردت تشغيله عند الطلب. بالنسبة للقيد، استخدم `self.with_context(skip_constraint=True).write(...)` وتحقق من `self.env.context.get('skip_constraint')` داخل طريقة القيد — لكن استخدم هذا بتحفظ، لأن تجاوز القيود قد يُخلّ بسلامة البيانات.
iWesabe Editorial Team

iWesabe Editorial Team

رؤى عملية حول Odoo ERP وامتثال ZATCA والعمليات الرقمية للشركات السعودية — بقلم فرق الاستشارات والمالية والهندسة في iWesabe.

عن iWesabe

مقالات ذات صلة