مقدمة
إذا فتحت طلب مبيعات في أودو ورأيت أسفل بيانات العميل قائمة بالأسطر، فذلك مثال عملي على علاقة One2many. هذه العلاقة تُستخدم بكثرة لبناء الهيكل البياني للبيانات داخل أودو، وفهمها ضروري لكل من يشتغل على تخصيص النظام أو تطويره.
في هذا الدليل سنشرح مفهوم One2many داخل نظام أودو، كيف تتفاعل مع ORM الخاص بأودو، متى تختار استخدامها في نموذجك، وكيف تضيفها سواء من خلال Odoo Studio دون برمجة أو عبر تعريفها في كود بايثون داخل وحدة مخصصة.
سواء كنت مستخدماً تجارياً تريد فهم شكل بياناتك أو مطوّراً تبحث عن دليل عملي لتعريف الحقول في أودو، ستجد هنا ما تحتاجه لتتعامل مع One2many بثقة.
ما هو حقل One2many في أودو؟
حقل One2many هو نوع علاقة في إطار عمل أودو يربط سجل أب واحد بعدد من سجلات الأطفال من نموذج آخر.
ببساطة: عميل واحد قد يملك فواتير متعددة، أو أمر بيع واحد يحتوي على عدة أسطر، أو مشروع واحد يضم مهاماً عدة. الحقل يوفّر نافذة تربط السجل الأب بجميع السجلات التابعة له.
في واجهة أودو يظهر One2many عادةً كقائمة داخل نموذج السجل الأب — جدول مضمن يسمح للمستخدمين بإضافة أو تعديل أو حذف السجلات الفرعية مباشرة دون مغادرة شاشة السجل الأب، وهذا هو النمط الذي يراه معظم المستخدمين يومياً.
كيف يختلف عن أنواع العلاقات الأخرى
في ORM الخاص بأودو توجد ثلاث علاقات رئيسية تُستخدم لبناء الروابط بين النماذج:
- Many2one: كل سجل يشير إلى سجل واحد في نموذج آخر (مثلاً: أمر بيع يشير إلى عميل واحد).
- One2many: سجل واحد مرتبط بعدة سجلات في نموذج آخر (مثلاً: أمر بيع مربوط بعدة أسطر).
- Many2many: علاقة متبادلة حيث يمكن لعدة سجلات على كلا الطرفين أن تتشارك الربط (مثلاً: منتج مرتبط بعدة وسمات وكل وسم مرتبط بعدة منتجات).
تُستخدم One2many عندما ينتمي السجل الفرعي لوالد واحد فقط. أما إذا احتجت مشاركة نفس السجل الفرعي بين أكثر من والد، فالحل الأنسب هو Many2many.
ميزة أساسية للـ One2many أنها حقل افتراضي لا يخزن بيانات مستقلة على جدول الأب؛ هي طريقة لعرض السجلات المرتبطة الموجودة في نموذج الطفل اعتماداً على حقل Many2one الموجود في ذلك الطفل.
كيف يعمل الحقل
بدلاً من إضافة عمود في جدول الأب، تُخزن الإشارة في جدول الطفل: كل صف في جدول الطفل يحتوي على مفتاح خارجي يشير إلى هوية السجل الأب.
هذه طريقة عمل ORM في أودو: حقل One2many يعتمد على وجود Many2one في النموذج المقابل، ويُعدّ بمثابة استعلام عكسي يجلب كل السجلات الفرعية التي تشير إلى السجل الأب الحالي.
العلاقة العملية بين One2many و Many2one
عند تعريف One2many في بايثون، يجب تحديد معلمين أساسيين:
- comodel_name: اسم النموذج الذي يحتوي السجلات الفرعية.
- inverse_name: اسم حقل الـ Many2one في نموذج الطفل الذي يشير للوالد.
كمثال عملي في وحدة مخصصة، قد تربط عقد خدمة بسجل تسليمات متعددة عبر حقل One2many مخصّص.
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
في المثال أعلاه، contract_id هو الـ Many2one الموجود في نموذج service.deliverable، ولا يمكن لحقل One2many أن يعمل بدونه — يجب وجود الحقل العكسي أولاً.
ماذا يحدث في قاعدة البيانات؟
البيانات الخاصة بعلاقة One2many تعيش في جدول نموذج الطفل. سجلات الأب لا تحتوي على عمود جديد؛ بدلاً من ذلك كل سجل فرعي يمتلك عمود مفتاح خارجي يُشير إلى رقم تعريف الأب.
عند تحميل سجل أب وعرض علاقة One2many، يقوم أودو بتنفيذ استعلام لجلب كل الصفوف في جدول الطفل حيث يتطابق المفتاح الخارجي مع معرف الأب الحالي — وهذا سلوك عادي في قواعد البيانات العلائقية وتقوم طبقة ORM بإدارته لك.
لهذا السبب يُصنّف One2many كحقل بايثون لا يضيف أعمدة فعلية في قاعدة البيانات، بل هو عرض محسوب للسجلات المرتبطة.
حالات استخدام عملية
أين يُستخدم One2many عملياً؟ فيما يلي أمثلة واقعية تبين كيف تنعكس العلاقات الأبوية على سير العمل في الشركات.
1. أوامر المبيعات وأسطرها
في موديول المبيعات هناك حقل One2many مثل order_line_ids على نموذج sale.order يربط بسطور الطلبات sale.order.line. كل سطر يحتوي على المنتج والكمية والسعر، ويمكن تحرير الأسطر مباشرة داخل نموذج أمر البيع.
2. الفواتير وأسطر الفاتورة
في المحاسبة يستخدم account.move One2many لإدارة الأسطر account.move.line. الفاتورة تجمع المجاميع من أسطرها الفرعية لتعرض المبلغ الإجمالي.
3. المشاريع والمهام
نموذج المشروع project.project يعرض المهام المتعلقة به عبر One2many. العرض يمكن أن يكون قائماً أو بنمط كانبان أو مخطط جانت بينما تبقى العلاقة نفسها في الخلفية.
4. جهات الاتصال والعناوين الفرعية
نموذج res.partner يملك حقل child_ids الذي يسرد جهات الاتصال المرتبطة بشركة. كل جهة اتصال فرعية تحتوي على parent_id تشير للشركة.
5. نماذج مخصصة في خدمات التصنيع
عند بناء وحدة مخصصة تكون One2many الخيار الطبيعي لربط سجل رئيسي بمجموعة من السجلات الفرعية — مثل طلب صيانة يرتبط بعدة قطع غيار أو دورة تدريبية لها تواريخ جلسات متعددة.
المرونة هذه تفسّر سبب اعتماد المطورين والمستشارين على One2many عند تصميم نماذج بيانات تلائم قطاعات متنوعة.
إنشاء الحقل أو تخصيصه
طرق إضافة One2many في أودو: طريقتان شائعتان — بدون كود عبر Odoo Studio أو بالتعريف المباشر في بايثون داخل وحدة مخصصة.
باستخدام Odoo Studio
في Studio لا يمكنك إنشاء One2many مباشرة على نموذج الأب قبل وجود Many2one في نموذج الطفل. السبب أن One2many يعتمد دائماً على وجود الحقل العكسي في الطفل.
الخطوات المُنصَح بها في Studio هي:
- افتح نموذج الطفل في Studio وأضف حقل Many2one يشير إلى نموذج الأب.
- بعد حفظ الحقل في الطفل عد إلى نموذج الأب في Studio.
- أضف حقل جديد واختر نوع "One2many" ثم اختر النموذج المرتبط واسم حقل الـ Many2one العكسي.
- بعدها سيظهر الحقل كقائمة مضمنة داخل نموذج الأب.
لهذا السبب يعتمد كثير من المستخدمين التجاريين على Studio لإنشاء علاقات One2many دون كتابة كود، وسلوك الحقول المضافة بهذه الطريقة يعادل الحقول المعرفة في بايثون.
عن طريق كود بايثون داخل وحدة مخصصة
للمطورين، تعريف One2many في بايثون يمنح تحكماً كاملاً في الاسم والسلوك وفلترة الدومين. نموذج كامل يوضح الربط يكون كالتالي:
from odoo import fields, models
class ServiceContract(models.Model):
_name = 'service.contract'
_description = 'Service Contract'
name = fields.Char(string='Contract Name', required=True)
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
class ServiceDeliverable(models.Model):
_name = 'service.deliverable'
_description = 'Service Deliverable'
contract_id = fields.Many2one(
comodel_name='service.contract',
string='Contract',
ondelete='cascade'
)
name = fields.Char(string='Description', required=True)
انتبه إلى أن حقل Many2one (contract_id) يُعرّف في نموذج الطفل أولاً، ويجب أن يتطابق اسم inverse_name مع اسم ذلك الحقل تماماً.
إنشاء الحقل عبر واجهة XML-RPC
إذا كنت تُدير حقول قاعدة البيانات برمجياً يمكن أيضاً تعريف One2many عبر الـ XML-RPC باستخدام نموذج ir.model.fields. القاعدة نفسها تطبق: أنشئ الـ Many2one أولاً ثم عرف One2many بالإشارة إلى اسم حقل العلاقة (relation_field)،
وهذه الطريقة مفيدة عند إدارة حقول عبر بيئات متعددة أو أتمتة مهام تخصيص أودو.
ممارسات مُستحسنة
من خبرتنا في تنفيذ أودو لعملاء مختلفين هناك مجموعة ممارسات بسيطة تُجنّب كثيراً من المشاكل عند التعامل مع One2many.
- دائماً ابدأ بإنشاء Many2one أولاً. لا يهم إن كنت تعمل في Studio أو بكود بايثون أو بالـ API، فعدم وجود الحقل العكسي سيمنع One2many من العمل.
- اتبع تسمية واضحة باستخدام لاحقة
_idsلأسماء حقول One2many — مثلline_idsأوtask_ids— فهذا يعكس أن الحقل يحتوي على مجموعة سجلات ويسهّل قراءة الكود. - حدد
ondelete='cascade'على Many2one عندما ترغب في حذف السجلات الفرعية تلقائياً عند حذف الأب، لتجنب بقايا سجلات متروكة في قاعدة البيانات. - اعرض فقط الأعمدة المهمة في القائمة المضمّنة داخل One2many. إظهار الكثير من الأعمدة يبطئ الواجهة ويشتت المستخدمين؛ يمكن استخدام خصائص اختيارية للأعمدة الثانوية.
- استخدم دومينات لتقييد السجلات الفرعية المعروضة عندما يكون نموذج الطفل مشتركاً بين نماذج آباء متعددة، فهكذا تبقي العرض مُنسقاً وملائماً للسياق.
- تعامل بحذر مع قوائم ضخمة. إذا كان للوالد آلاف السجلات الفرعية، تجنّب تحميلها كلها داخل نموذج واحد — فكر في إظهار نتائج مُصفّحة أو تحويل العرض إلى قائمة منفصلة مع ترقيم صفحات.
أخطاء شائعة يجب تجنّبها
الأخطاء الشائعة ومصادر المشاكل
نسيان وجود Many2one العكسي
أكثر الأخطاء شيوعاً هو محاولة تعريف One2many دون تعريف Many2one في نموذج الطفل أولاً. سيظهر خطأ لأن اسم inverse_name غير موجود؛ تحقق دائماً من وجود الحقل العكسي بنفس الاسم.
استخدام صيغة كتابة خاطئة عند التعديل
عند تحديث One2many عبر الكود أو الواجهة البرمجية يجب استخدام صيغة أوامر Odoo الخاصة بدل تعيين قائمة بايثون عادية. لا يمكن تعيين قائمة مباشرة للسجلات المرتبطة.
- مثال:
(0, 0, values_dict)لإنشاء سجل فرعي جديد. (1, child_id, values_dict)لتعديل سجل فرعي موجود.(2, child_id, 0)لحذف سجل فرعي موجود من القاعدة.(4, child_id, 0)لإضافة سجل فرعي موجود إلى العلاقة دون تغييره.(5, 0, 0)لفصل كل السجلات الفرعية عن الأب دون حذفها من القاعدة.
محاولة كتابة شيء مثل record.line_ids = [1, 2, 3] لن تعمل كما تتوقع داخل ORM.
الخَلط بين One2many و Many2many
إذا كان مطلوباً أن يرتبط سجل فرعي بأكثر من والد في آن واحد، فـ One2many ليست مناسبة — استخدم Many2many. محاولة المحاكاة عبر One2many تؤدي إلى تكرار بيانات وتَعرّض سلامة البيانات للخطر.
مشكلة الأداء مع قوائم كبيرة
عند عرض مئات أو آلاف السطور داخل One2many سيتباطأ تحميل الصفحة بشكل ملحوظ. هذا شائع في الفواتير أو حركات المخزون؛ استخدم حدّاً بالعرض أو حول المستخدمين لقائمة منفصلة.
سجلات يتيمة بعد الحذف
حذف السجل الأب بدون سياسة حذف واضحة (مثل ondelete='cascade') قد يترك سجلات فرعية بلا مرجع، ما يؤدي إلى تراكم بيانات غير مرغوب فيها ومشكلات في التقارير. صمّم سياسة حذف واضحة منذ البداية.
الخلاصة
حقل One2many هو ركيزة أساسية في نموذج بيانات أودو ونمط العمل ضمن النظام. من أوامر البيع إلى أسطر الفواتير والمهام في المشاريع، فهم علاقة One2many وارتباطها بالـ Many2one يجعل قراءة وبناء نموذج البيانات أمراً بسيطاً وأكثر أماناً.
سواء كنت تهيئ أودو لشركتك عبر Studio أو تبني وحدة مخصصة أو تعدّل بنية البيانات، سيبقى One2many أداة متكررة. معرفة متى وكيف تُعرّفها وتجنّب الأخطاء الشائعة يوفر وقتاً كبيرة ويمنع مشاكل تكامل البيانات لاحقاً.
للمزيد من الدروس والمراجع التقنية حول حقول أودو وواجهات البرمجة، تصفّح مقالات قسم البيانات وAPI في موقعنا.
هل تحتاج مساعدة في تنفيذ أودو؟
تقوم Dasolo بمساعدة الشركات على تنفيذ وتخصيص وتحسين أودو ليتلاءم مع احتياجات أعمالها. سواء أردت تصميم نموذج بيانات، بناء وحدات مخصصة، إنشاء حقول أو الحصول على قيمة أكبر من نظامك الحالي، فريقنا جاهز لدعمك.
إذا كانت لديك أسئلة عن مشروع أودو الخاص بك أو تريد مناقشة أفضل طريقة لتنظيم بياناتك، تواصل معناوسنكون سعداء بتقديم المساعدة.