كيفية تعيين صورة افتراضية لحقل ثنائي في أودو (16–19)
مرجع شامل للمطوّر لتعيين الصور الافتراضية على حقول أودو الثنائية وحقول الصور: fields.Image مقابل fields.Binary وأسلوب file_open + base64 وطرق _default_image وأصول الوحدة الثابتة وخاصية placeholder لعنصر واجهة الصورة. موثق لأودو 18 و19.
تعيين صورة افتراضية على حقل ثنائي أو حقل صورة في أودو متطلب شائع: صورة رمزية افتراضية لجهات الاتصال الجديدة، أو صورة منتج عنصر نائب، أو شعار الشركة مُعبأ مسبقًا في سجل جديد. تغيّر نهج التنفيذ بشكل كبير بين أودو 14 وأودو 16 مع إدخال `fields.Image` كنوع حقل صورة مخصص. يغطي هذا الدليل كلًا من نهج `fields.Image` الحديث (أودو 16+) ونهج `fields.Binary` القديم، حتى تتمكن من التعامل مع الوحدات الجديدة والكود القديم على حدٍّ سواء.
fields.Image مقابل fields.Binary — أيهما تستخدم
| الخاصية | fields.Image | fields.Binary |
|---|---|---|
| متاح منذ | أودو 13 (مستقر، موصى به من أودو 14+) | جميع إصدارات أودو |
| التخزين | بايتات مُرمَّزة بـ base64 في قاعدة البيانات (مثل Binary) | بايتات مُرمَّزة بـ base64 في قاعدة البيانات |
| تغيير الحجم التلقائي | نعم — max_width وmax_height يُغيّران الحجم تلقائيًا عند الحفظ | لا — يُخزّن أي بايتات تُمرّرها |
| صورة مصغرة تلقائية | نعم — يُنشئ أودو حقول shadow لـ image_128 وimage_256 تلقائيًا | لا — حقول الصورة المصغرة يدوية |
| الأفضل لـ | أي صورة (صورة رمزية، صورة منتج، شعار شركة). استخدم هذا في جميع التطوير الجديد. | الملفات الثنائية غير الصورية (مرفقات PDF، تصدير Excel). يُستخدم أيضًا في وحدات أودو 12 والإصدارات الأقدم. |
تخزين الصور الافتراضية في وحدتك
يجب أن يشحن ملف الصورة الافتراضية داخل مجلد وحدتك. الموقع القياسي هو `static/img/` — هذا يعكس النمط المستخدم في وحدات أودو الخاصة (مثلًا `mail/static/img/default_image.png`). يُقرأ الملف عند تثبيت الوحدة (أو عند استدعاء الدالة الافتراضية لسجل جديد) ويتم تحويله إلى سلسلة base64 مُخزَّنة في قاعدة البيانات.
my_module/
├── __manifest__.py
├── __init__.py
├── models/
│ └── my_model.py
├── static/
│ └── img/
│ └── default_avatar.png ← default image file goes hereتعيين صورة افتراضية على fields.Image (الحديث — أودو 14+)
يستخدم النمط الموصى به دالة على مستوى الوحدة لقراءة ملف الصورة وإعادته كقيمة بايتات مُرمَّزة بـ base64. ثم تُمرَّر هذه الدالة إلى معامل `default=` لـ `fields.Image`.
# models/my_model.py
import base64
from odoo import fields, models
from odoo.modules.module import get_module_resource
def _default_image():
"""Read the bundled default avatar and return it as base64 bytes."""
img_path = get_module_resource("my_module", "static", "img", "default_avatar.png")
with open(img_path, "rb") as f:
return base64.b64encode(f.read())
class MyModel(models.Model):
_name = "my.model"
_description = "My Model"
name = fields.Char(required=True)
image_1920 = fields.Image(
string="Image",
max_width=1920,
max_height=1920,
default=_default_image, # ← pass the function object, not the call
)
# Odoo auto-generates image_128 and image_256 shadow fields from image_1920تعيين صورة افتراضية على fields.Binary (النمط القديم)
في وحدات أودو 12 و13 (أو الكود القديم الذي تصونه)، ينطبق نفس النمط باستخدام `fields.Binary`. الفرق الوحيد هو إعلان الحقل — دالة `_default_image` واستدعاء `get_module_resource` متطابقان.
# models/my_model.py (Odoo 12/13 legacy pattern)
import base64
from odoo import fields, models
from odoo.modules.module import get_module_resource
def _default_image():
img_path = get_module_resource("my_module", "static", "img", "default_avatar.png")
with open(img_path, "rb") as f:
return base64.b64encode(f.read())
class MyModel(models.Model):
_name = "my.model"
_description = "My Model"
name = fields.Char(required=True)
image = fields.Binary(
string="Image",
attachment=True, # store in ir.attachment instead of database column
default=_default_image,
)
image_medium = fields.Binary(
string="Medium Image",
attachment=True,
)
image_small = fields.Binary(
string="Small Image",
attachment=True,
)
# In legacy Odoo 12/13, you had to generate resized variants yourself.
# In Odoo 14+ with fields.Image, this is automatic.بديل: استخدام file_open بدلًا من get_module_resource
بديل أقصر قليلًا هو `odoo.tools.misc.file_open`، الذي يقبل مسارًا نسبيًا للوحدة ويتعامل مع تخطيطات الوحدة في التطوير والإنتاج (المُجمَّعة بـ zip). كلا النهجين يعمل — اختر الذي يطابق الأسلوب الموجود في قاعدة الكود الخاصة بك.
# Alternative using file_open
import base64
from odoo import fields, models
from odoo.tools.misc import file_open
def _default_image():
# file_open path: <module_name>/<relative_path_within_module>
with file_open("my_module/static/img/default_avatar.png", "rb") as f:
return base64.b64encode(f.read())
class MyModel(models.Model):
_name = "my.model"
_description = "My Model"
image_1920 = fields.Image(
string="Image",
max_width=1920,
max_height=1920,
default=_default_image,
)خاصية placeholder — إظهار صورة افتراضية في العرض بدون تخزينها
أحيانًا تريد عنصرًا نائبًا مرئيًا في عرض النموذج عندما لا تكون هناك صورة محددة، دون ملء حقل قاعدة البيانات مسبقًا لكل سجل جديد. تفعل خاصية `placeholder` على عنصر `` ذلك بالضبط — تعرض صورة احتياطية في واجهة المستخدم فقط، ولا تكتب أي شيء في قاعدة البيانات.
<!-- views/my_model_views.xml -->
<record id="view_my_model_form" model="ir.ui.view">
<field name="name">my.model.form</field>
<field name="model">my.model</field>
<field name="arch" type="xml">
<form string="My Model">
<sheet>
<field name="image_1920"
widget="image"
class="oe_avatar"
placeholder="/my_module/static/img/default_avatar.png"/>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>ملء الصورة الافتراضية بأثر رجعي على السجلات الحالية
عند إضافة `default=_default_image` إلى حقل موجود في وحدة نشرت بالفعل، ينطبق الافتراضي فقط على السجلات الجديدة التي تُنشأ بعد تحديث الوحدة. ستظل السجلات الحالية التي لم يكن لها صورة قبل التحديث بدون صورة. لملء السجلات الحالية بأثر رجعي، أضف `post_init_hook` أو نص ترحيل.
# __manifest__.py
{
"name": "My Module",
"version": "17.0.1.1.0",
"post_init_hook": "post_init_hook", # ← add this
...
}
# __init__.py
from .hooks import post_init_hook # ← add this
# hooks.py (new file)
import base64
from odoo.modules.module import get_module_resource
def post_init_hook(env):
"""Back-fill default image on records that have none."""
img_path = get_module_resource("my_module", "static", "img", "default_avatar.png")
with open(img_path, "rb") as f:
default_image = base64.b64encode(f.read())
records_without_image = env["my.model"].search([("image_1920", "=", False)])
records_without_image.write({"image_1920": default_image})استكشاف الأخطاء وإصلاحها
| العَرَض | السبب | الحل |
|---|---|---|
| تُنشأ السجلات الجديدة بدون الصورة الافتراضية | استُدعيت الدالة الافتراضية عند تعريف الفئة بدلًا من تمريرها كقابل للاستدعاء — مثلًا default=_default_image() بدلًا من default=_default_image | احذف الأقواس: default=_default_image (مرّر كائن الدالة، وليس قيمة إعادتها). |
| FileNotFoundError أو get_module_resource تُعيد None | مسار ملف الصورة خاطئ — الملف غير موجود في static/img/default_avatar.png نسبةً إلى جذر الوحدة | تحقق من وجود الملف: ls <addons_path>/my_module/static/img/. تحقق من الأخطاء الإملائية في اسم الملف. تُعيد get_module_resource القيمة None (وليس خطأ) إذا لم يُعثر على الملف — أضف فحصًا: assert img_path, 'Default image not found'. |
| تظهر الصورة الافتراضية للسجلات الجديدة في التطوير لكن ليس في الإنتاج | لم تُدرج الملفات الثابتة للوحدة في النشر (مفقودة من commit git أو بناء أودو.sh) | تأكد أن static/img/default_avatar.png مُثبَّت في المستودع (git status). على أودو.sh، تحقق من سجل البناء — يجب أن يكون الملف في شجرة المستودع، وليس مُدرجًا في .gitignore. |
| يُخزّن حقل الصورة سلسلة base64 كنص بدلًا من عرض الصورة | الحقل fields.Binary بدون widget='image' في XML العرض، أو الصورة مُخزَّنة عن طريق الخطأ كـ Python str بدلًا من bytes | 1) أضف widget='image' إلى عنصر <field> في العرض. 2) في الدالة الافتراضية، تأكد أن base64.b64encode() تُعيد bytes. لا تفكك إلى str — يتوقع fields.Image وfields.Binary قيمة bytes. |
| حقول shadow image_128/image_256 فارغة حتى لو كانت image_1920 لها قيمة | كُتبت الصورة مباشرةً إلى قاعدة البيانات (مثلًا عبر سكريبت SQL أو كتابة ORM منخفضة المستوى تجاوزت منطق حساب الحقل) دون تشغيل منطق تغيير حجم fields.Image | أعد كتابة الحقل عبر ORM: records.write({'image_1920': the_image_bytes}). تُشغّل كتابة ORM تغيير الحجم وملء حقول shadow. عمليات الإدراج المباشرة عبر SQL تتجاوز هذا. |
ملاحظات الإصدار
| إصدار أودو | التغييرات الرئيسية المؤثرة على الصورة الافتراضية على الحقول الثنائية/الصور |
|---|---|
| أودو 14 و15 | استقر fields.Image وأصبح موصى به لجميع حقول الصور الجديدة. لا يزال نمط _default_image في fields.Binary يعمل لكنه يُعدّ قديمًا. حقول shadow image_128 وimage_256 تُنشأ تلقائيًا بواسطة fields.Image. get_module_resource متاحة في odoo.modules.module. |
| أودو 16 | نُقلت file_open من odoo.tools إلى odoo.tools.misc — حدّث الاستيرادات إذا كنت تهاجر من أودو 15. fields.Image ونمط default= لم يتغيرا. أصبحت اتفاقية التسمية image_1920/image_128/image_256 للصور الرمزية/صور جهات الاتصال معيارًا عبر قاعدة كود أودو. |
| أودو 17 | لا توجد تغييرات كاسرة على fields.Image أو نمط default=. post_init_hook ونصوص الترحيل للملء بأثر رجعي لم تتغير. عنصر واجهة الصورة في عروض النماذج يقبل placeholder كما كان من قبل. |
| أودو 18 و19 | لا توجد تغييرات كاسرة. fields.Image مع default=_default_image يعمل بشكل مطابق. مسارات get_module_resource وfile_open لم تتغير. تغيير الحجم التلقائي max_width/max_height وإنشاء حقول shadow لم يتغيرا. |
تحتاج تطويرًا مخصصًا لأودو لأعمالك؟
يبني فريق أودو المعتمد لدينا وحدات مخصصة ويُهاجر قواعد الكود القديمة إلى أودو 17/18 ويُقدّم تنفيذات كاملة في جميع أنحاء المملكة العربية السعودية — بما في ذلك تكامل ZATCA المرحلة الثانية وواجهة المستخدم العربية وGOSI وامتثال WPS-Mudad.
الأسئلة الشائعة
كيف أعيّن صورة افتراضية لحقل fields.Image في أودو؟
ما الفرق بين fields.Image وfields.Binary لتخزين الصور في أودو؟
أين يجب أن أُخزّن ملف الصورة الافتراضية داخل وحدة أودو الخاصة بي؟
كيف أضيف صورة عنصر نائب في عرض نموذج أودو دون تخزين صورة افتراضية في قاعدة البيانات؟
كيف أملأ صورة افتراضية بأثر رجعي على السجلات الحالية بعد إضافة افتراضي لحقل صورة؟
لماذا تظهر صورتي الافتراضية في التطوير لكن ليس بعد النشر على أودو.sh؟

iWesabe Editorial Team
رؤى عملية حول Odoo ERP وامتثال ZATCA والعمليات الرقمية للشركات السعودية — بقلم فرق الاستشارات والمالية والهندسة في iWesabe.
مقالات ذات صلة
كيفية عرض صورة في QWeb أودو — الويب والتقارير والبوابة (أودو 16–19)
مرجع شامل للمطوّر لعرض الصور في قوالب QWeb لأودو: وحدة التحكم /web/image وأنماط t-att-src وt-field widget='image' وصور تقارير PDF واستكشاف الأخطاء الشائعة. موثق لأودو 18 و19.
أمان الوصول في أودو — ir.model.access.csv وقواعد السجلات (أودو 16–19)
مرجع شامل للمطوّر حول نظام الأمان ثنائي الطبقة في أودو: صلاحيات CRUD على مستوى النموذج عبر ir.model.access.csv والتصفية على مستوى السجل عبر ir.rule. موثق لأودو 18 و19.
كيفية نشر وحدات المجتمع على أودو.sh (أودو 16–19)
مرجع شامل للمطوّر لنشر الوحدات المخصصة ووحدات مجتمع OCA على أودو.sh: إعداد مستودع GitHub وسير العمل بالفروع والوحدات الفرعية ومتطلبات.txt وسجلات البناء والترقية من التدريج إلى الإنتاج. موثق لأودو 18 و19.