مقدمة
حقل Many2One يمثل الرابط البسيط والمتكرر الذي يبني العلاقات بين السجلات في أودو. كل مرة تربط فيها فاتورة بعميل، أو تعيّن منتجًا لفئة، أو تربط مهمة بمشروع، فأنت تستخدم نفس الفكرة: سجل واحد يشير إلى سجل آخر منفرد. فهم هذا الحقل ضروري لمن يبغون ضبط نظام أودو بشكل عملي ومستقر.
من زاوية العمل، هذه الحقول هي ما يحول وحدات أودو المتفرقة إلى نظام مترابط. بدونها تظل كل وحدة جزيرة خاصة بها؛ مع وجودها تتدفق المعلومات بسلاسة بين المستندات، والمستخدمون يتنقلون بين العلاقات كأنهم يتنقلون داخل نفس التطبيق، من دون القلق حول هيكل قاعدة البيانات.
هذا الدليل يشرح ما الذي يخزّنه حقل Many2One، كيف يتصرّف داخل ORM وعند عرضه للمستخدم، طرق إنشائه سواء عبر أودو ستوديو أو كود بايثون، وأمثلة عملية في مجالات مثل المبيعات، المخزون، المحاسبة وإدارة المشاريع.
ما هو حقل Many2One في أودو
في بنية ORM الخاصة بأودو، حقل Many2One يضع رابطًا من سجل واحد إلى سجل واحد في نموذج آخر. التسمية تأتي من زاوية النموذج الحالي: سجلات كثيرة هنا يمكن أن تشير إلى سجل واحد هناك. مثال عملي: أوامر البيع العديدة تشير إلى عميل واحد، أو منتجات متعددة تنتمي إلى فئة واحدة.
على مستوى قاعدة البيانات يُخزن الحقل كمفتاح خارجي في جدول النموذج الحالي. لو ارتبطت أمر بيع بالعميل رقم 42، فعمود partner_id في جدول sale_order سيحمل القيمة 42. أودو يتولى عملية الربط واسترجاع اسم السجل المرتبط تلقائيًا.
في واجهة المستخدم يظهر الحقل عادة كقائمة منسدلة قابلة للبحث (typeahead). يكتب المستخدم جزءًا من الاسم فتقوم أودو بتصفية النتائج فورًا؛ اختيار سجل يعين الرابط وتظهر للمستخدم اسم السجل المرتبط بدلاً من رقم داخلي — وهذا ما يجعل التجربة بديهية.
شكل تعريف الحقل في نموذج بايثون
from odoo import fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
x_account_manager_id = fields.Many2one(
comodel_name='res.users',
string='Account Manager',
ondelete='set null',
)
المعلمة comodel_name تحدد النموذج الهدف الذي يشير إليه الحقل، وondelete تحدد المطلوب حدوثه إذا حُذف السجل المرتبط: cascade للحذف المتتالي، set null لمسح الرابط، وrestrict لمنع الحذف إذا كان هناك مراجع.
في أودو ستوديو ستجد الحقل تحت اسم Many2One في أداة إضافة الحقول. عند سحبه إلى النموذج تختار النموذج الهدف من قائمة، وستوديو ينشئ الحقل تلقائيًا — طريقة سريعة لمنح النموذج علاقة دون كتابة كود.
كيف يعمل الحقل
قراءة الحقل من داخل ORM تعيد مجموعة سجلات تحوي السجل المرتبط، وإن كان الحقل فارغًا فستحصل على مجموعة فارغة. يمكن الوصول مباشرة إلى حقول السجل المرتبط عبر النقاط في بايثون:
order = self.env['sale.order'].browse(1) customer_name = order.partner_id.name customer_city = order.partner_id.city
هذه القدرة على التنقل المتسلسل تمنح المطور سهولة: لا حاجة لكتابة استعلامات JOIN يدوية؛ الـ ORM يتكفل بعمل قواعد البيانات في الخلفية.
عند الوصول للحقل عبر واجهة XML-RPC ستستلم قائمة من عنصرين: رقم الـ ID واسم العرض، مثل [42, "Acme Corp"]. وإذا كان الحقل فارغًا فتعيد الواجهة False. هذه الصيغة مهمة عند معالجة الردود برمجيًا.
السمات الرئيسية للحقل
فيما يلي الخصائص الأهم التي يمكنك ضبطها على حقل Many2One داخل إطار عمل أودو:
- comodel_name: الاسم التقني للنموذج الهدف — هذا هو الحقل الإلزامي الوحيد.
- ondelete: سلوك الحقل عند حذف السجل المرتبط. الخيارات: 'cascade'، 'set null'، و'restrict'. القيمة الافتراضية عادة 'set null'.
- domain: شرط تحدد به أي السجلات يمكن اختيارها من القائمة. مثال: domain=[('customer_rank', '>', 0)] يقصر الاختيار على العملاء فقط.
- context: قيم سياق إضافية تُمرر عند فتح السجل المرتبط أو نافذة الاختيار. مفيد لملء قيم افتراضية عند إنشاء سجل جديد مرتبط.
- required: يجعل الحقل إلزاميًا — لا يمكن حفظ السجل بدون تحديده.
- readonly: يمنع المستخدمين من تعديل الرابط يدويًا، مفيد عندما يُحدد الحقل برمجياً.
- delegate: عند تفعيله True تصبح حقول النموذج المرتبط متاحة مباشرة على النموذج الحالي — يستخدم لوراثة النماذج وليس للعلاقات الاعتيادية.
كيف يظهر الحقل في الواجهات
في نماذج الإدخال يظهر الحقل كقائمة قابلة للبحث حيث يمكن للمستخدمين كتابة أجزاء من الاسم لتصفية النتائج، كما يوجد سهم لفتح السجل المرتبط مباشرة للتنقل السريع بين المستندات.
في قوائم السجلات يعرض الحقل اسم السجل المرتبط ويدعم التجميع (group-by): يمكنك جمع أوامر البيع حسب العميل أو المهام حسب المشروع، وهي ميزة مستخدمة بكثرة في تقارير أودو.
في واجهات البحث يمكن استخدام Many2One كمرشح أو معيار للتجميع. عندما تصفّي الفرص في CRM بحسب العميل فأنت تتعامل مع حقل من هذا النوع.
العلاقة العكسية One2Many
لكل Many2One يوجد عكس طبيعي هو One2Many. إن ربطت أوامر البيع بعميل عبر Many2One فصفحة العميل يمكن أن تعرض قائمة بكل الأوامر المرتبطة به عبر One2Many. من الأفضل دائمًا تعريف الطرفين في التطوير لأن ذلك يسهل تنقّل المستخدمين بين الجانبين دون بحث منفصل. One2Many لا تضيف عمودًا جديدًا لقاعدة البيانات بل تُحتسب انطلاقًا من المفتاح الخارجي في الجانب الآخر.
حالات استخدام عملية
حقل Many2One حاضر في كل تركيب قياسي لأودو. فيما يلي خمسة أمثلة تطبيقية من سيناريوهات أعمال حقيقية.
CRM: ربط العملاء المحتملين بمسؤولين مبيعات
في وحدة CRM، لكل فرصة حقل user_id كثير إلى واحد يربطها بمستخدم النظام (مندوب المبيعات). المديرون يستطيعون تصفية الخطوط بحسب المندوب، قياس نسب التحويل لكل ممثل، وتوزيع الفرص دفعة واحدة. إضافة حقل Many2One آخر لتمثيل مدير حساب أو شريك ثانٍ أمر بسيط ويمكن تنفيذه دون تطوير معقد.
المبيعات: ربط الأوامر بالعملاء وقوائم الأسعار
في أمر البيع عادة توجد حقول Many2One أساسية مثل partner_id (العميل) وpricelist_id (قائمة الأسعار). عند اختيار عميل يمكن لأودو تعبئة قائمة الأسعار وشروط الدفع وعناوين التسليم آليًا عبر طرق onchange التي تعتمد على قيمة Many2One. هذه الآلية تحسّن تجربة المستخدم وتسرّع إدخال المستندات.
المخزون: المنتجات والفئات
كل منتج ينتمي إلى فئة منتجات عبر Many2One (مثل الحقل categ_id). فئة المنتج تتحكم في حسابات التكلفة والإيرادات وأساليب التقييم واستراتيجية السحب في المستودعات. وضع الفئة الصحيحة مهم للمخرجات المحاسبية، وMany2One يجعل التعيين بسيطًا وقابلًا لإعادة الاستخدام عبر مئات المنتجات.
المحاسبة: قيود اليومية واليورنالات
كل قيد محاسبي يرتبط بيورنال محاسبي عبر Many2One (journal_id). اليورنال يحدد تسلسل الأرقام ونوع القيد والحسابات الافتراضية. اختيار يورنال خاطئ يؤدي إلى إدخالات في القسم غير الصحيح من الدفتر العام؛ إذًا الحقل هنا نقطة تحكم أساسية للدقة المحاسبية.
إدارة المشاريع: المهام والمشروعات
في وحدة المشروع، كل مهمة مرتبطة بمشروع عبر حقل Many2One (project_id). هذا الربط يحدد سير المراحل، صلاحيات الوصول، وكيف تُحتسب الجداول الزمنية. للشركات التي تفوّتر حسب المشروع، فإن ربط السطور الزمنية والمهام بالمشروع هو العمود الفقري لتتبع الإيرادات وإصدار الفواتير.
إنشاء أو تخصيص حقل Many2One
ثلاث طرق رئيسية لإضافة حقل Many2One للنموذج تبعًا للسياق التقني وطريقة النشر.
باستخدام أودو ستوديو (بدون كود)
أودو ستوديو هي أداة التخصيص منخفضة الكود المدمجة. خطوات إضافة الحقل دون برمجة:
- افتح أودو ستوديو من القائمة الرئيسية.
- اذهب إلى النموذج الذي تريد إضافة الحقل إليه.
- اسحب حقل Many2One من أداة اختيار الحقول إلى النموذج.
- في لوحة الخصائص اختر النموذج الهدف من القائمة.
- عيّن التسمية وأضف أي فلتر domain لتقييد السجلات الممكن اختيارها.
- احفظ وأغلق ستوديو.
ستوديو ينشئ الحقل باسم يبدأ عادة x_studio_ ويضيفه إلى العرض تلقائيًا. يصبح الحقل جاهزًا للاستخدام فورًا — أسهل وأسرع طريقة لإضافة علاقة دون معرفة برمجية عميقة.
باستخدام بايثون في موديل مخصص
للمطورين الذين يبنون موديلات أودو مخصصة، تُعرّف حقول Many2One في بايثون مباشرةً. هذه الطريقة هي المفضلة عندما تحتاج لتتبع التغييرات في نظام مراقبة الإصدارات ونشر عبر بيئات متعددة:
from odoo import fields, models
class ProjectTask(models.Model):
_inherit = 'project.task'
x_client_contact_id = fields.Many2one(
comodel_name='res.partner',
string='Client Contact',
domain=[('type', '=', 'contact')],
ondelete='set null',
help='The main contact at the client for this task.',
)
بعد تعريف الحقل في الموديل أدرجه في ملف العرض XML ثم نفّذ ترقية للموديول لتطبيق التغييرات على قاعدة البيانات. هذا يمنحك تحكمًا كاملاً في الدومينات، سلوك ondelete، وتكامل الحقل مع الحقول المحسوبة والقيود.
من الممارسات الجيدة عند تخصيص Many2One إضافة الحقل المقابل One2Many على النموذج المرتبط لتمكين التنقل بالعكس:
class ResPartner(models.Model):
_inherit = 'res.partner'
x_task_ids = fields.One2many(
comodel_name='project.task',
inverse_name='x_client_contact_id',
string='Related Tasks',
)
باستخدام واجهة XML-RPC
إذا كنت تدير التعديلات برمجيًا من خلال سكربت نشر أو دفتر ملاحظات بعيد، يمكنك إنشاء حقول Many2One عبر XML-RPC أيضاً:
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_client_segment_id',
'field_description': 'Client Segment',
'model_id': model_id,
'ttype': 'many2one',
'relation': 'res.partner.category',
'on_delete': 'set null',
'state': 'manual',
}]
)
مفتاح relation يحدد النموذج الهدف، وon_delete يحدد السلوك عند الحذف. عند الإنشاء عبر API تذكّر أيضًا تعريف One2Many المقابل حتى تعمل التنقلات من الجانبين — قاعدة ضرورية في إعدادات النشر البرمجية المنظمة.
ممارسات موصى بها
1. عرف دائمًا One2Many العكسي
عند إضافة Many2One احرص على إنشاء One2Many في النموذج المرتبط أيضًا. لا يضيف هذا عمودًا في قاعدة البيانات لكنه يحسّن تجربة المستخدم بشكل كبير؛ دون ذلك ستفقد القدرة على رؤية جميع السجلات المرتبطة من صفحة السجل المرتبط.
2. استخدم فلترة domain لتقييد القوائم
حقل Many2One إلى res.partner يتيح الوصول لجميع الشركاء افتراضيًا — عملاء وبائعون وعناوين تسليم وموظفون. إن كان المقصود اختيار عملاء فقط فضع domain مناسبًا لتقليل الضجيج ومنع الاختيارات الخاطئة.
3. اختر ondelete بعناية
سلوك ondelete قد يسبب نتائج غير متوقعة. cascade يحذف السجلات المرتبطة معه مما قد يؤدي لفقدان بيانات كبير. غالبًا ما يكون set null الخيار الأكثر أمانًا، بينما تستخدم restrict عندما لا ينبغي حذف السجل طالما أن هناك مراجعًا إليه.
4. لا تكرر البيانات بدلاً من استخدام علاقة
خطأ شائع هو إضافة حقل نصي لحفظ اسم شركة أو فئة بدلًا من استخدام Many2One إلى النموذج المناسب. التكرار يؤدي لمشكلات في التصفية والتجميع وتذبذب البيانات عند تغيير الأسماء. إن كانت القيم موجودة في نموذج آخر فاستخدم Many2One.
5. استخدم context لملء القيم افتراضيًا عند إنشاء سجلات مرتبطة
سياق الحقل يمكّنك من تمرير قيم افتراضية عند إنشاء سجل جديد من نافذة الاختيار؛ على سبيل المثال ملء مشروع افتراضي عند إنشاء جهة اتصال من مهمة، مما يقلل إدخال البيانات اليدوي ويحافظ على اتساق المعلومات.
أخطاء شائعة يجب تجنبها
نسيان One2Many العكسي
إضافة Many2One بدون تعريف One2Many المقابل أكثر هفوة شائعة. النتيجة رابط باتجاه واحد فقط: يمكنك رؤية السجل المرتبط من النموذج الحالي لكن من جهة السجل المرتبط لا ترى من يشير إليه. هذا يؤدي لشكاوى المستخدمين وبناء تقارير مخصصة للتعويض عن نقص التنقل.
استخدام cascade للحذف دون تفكير
تعيين ondelete='cascade' على علاقة تربط سجلات تشغيلية بسجل رئيسي يمكن أن يؤدي لفقدان بيانات كارثي — حذف أو أرشفة فئة منتجات قد يمسح كل المنتجات المرتبطة إن وُضعت cascade. في غالب الحالات set null أو restrict أكثر أمانًا.
عدم التحقق من وجود قيمة قبل التصفح في بايثون
عند كون الحقل فارغًا، قراءته في بايثون تعطي مجموعة سجلات فارغة وهي تُقيّم كـ False. إذا كتبت order.partner_id.name بدون التحقق قد تحصل على سلسلة فارغة، وهذا قد يكون مقبولًا لكن عند التصفّح لعدة مستويات (order.partner_id.country_id.name) فإن أي رابط مفقود سيعطي نتيجة فارغة تُخفي أخطاء منطقية في تقارير أو رسائل. افحص وجود السجلات عند الضرورة.
الإشارة إلى النموذج الخطأ
نموذج res.partner يجمع عملاء وبائعين وجهات اتصال معًا. Many2One إلى res.partner بدون domain يعرض كل هذه الفئات. إن أردت الحقل للزبائن فقط فحدد domain مناسبًا وإلا سيرى فريق المبيعات عناوين تسليم أو جهات داخلية تبدو صحيحة لكنها خاطئة للسياق.
الإفراط في استخدام Many2One عندما يكفي Selection
إذا كانت القيم محدودة وثابتة، فحقل Selection أبسط وأكثر كفاءة من Many2One. Many2One يتطلب نموذجًا منفصلاً ويسبب JOIN في الاستعلامات. لحالات مثل حالة الطلب مع 3-4 خيارات، Selection أنسب. استعمل Many2One عندما تكون القيم كثيرة أو يديرها المستخدمون أو تُشارك بين نماذج متعددة.
خلاصة
حقل Many2One هو قلب الربط بين بيانات أودو عبر الوحدات. فهمه لا يقتصر على المطورين فقط؛ المحللون الوظيفيون والاستشاريون والمستخدمون المتقدمون يستفيدون من معرفة متى وأين وكيف يستخدم هذا الحقل ليصمموا نموذج بيانات نظيف وقابل للصيانة.
النقاط الأساسية التي يجب تذكّرها: عرّف One2Many العكسي لتمكين التنقّل، ضع فلترات domain لتبقي القوائم مناسبة، اختر سلوك ondelete بعناية لتجنب حذف بيانات غير مقصود، وتجنّب Many2One عندما يكفي حقل Selection أبسط.
سواء كنت تضيف حقل عبر أودو ستوديو، تكتب موديل بايثون مخصص، أو تدير النموذج عبر XML-RPC، فإن ضبط الحقول العلائقية بشكل صحيح منذ البداية يجعل التنفيذ أكثر موثوقية وأسهل للصيانة على المدى الطويل.
في Dasolo نساعد الشركات على تنفيذ وتخصيص وتحسين أودو عبر الأقسام المختلفة. سواء رغبت في تصميم نموذج بيانات منظم، إضافة علاقات على النماذج، أو بناء موديل كامل من الصفر، فريقنا جاهز لدعمك. تواصل معنا ولنتحدث عن مشروع أودو الخاص بك.