النماذج (Models)
النموذج هو المصدر الوحيد والموثوق للمعلومات حول بياناتك. يحتوي على الحقول والسلوكيات الأساسية للبيانات التي تقوم بتخزينها. بشكل عام، يتوافق كل نموذج مع جدول قاعدة بيانات واحد.
الأساسيات:
- كل نموذج هو فئة (Class) بايثون تفرّع من
django.db.models.Model
. - كل سمة من سمات النموذج تمثل حقلًا في قاعدة البيانات.
- من خلال ذلك، يوفر Django واجهة برمجة تطبيقات (API) للوصول إلى قاعدة البيانات يتم توليدها تلقائيًا.
مثال سريع:
النموذج التالي يعرّف شخصًا (Person)، يحتوي على اسمه الأول (first_name
) واسم العائلة (last_name
):
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
first_name
و last_name
هما حقول النموذج. يتم تحديد كل حقل كخاصية (attribute) من خصائص الفئة، وكل خاصية تمثل عمودًا في قاعدة البيانات.
النموذج السابق لـ Person
سينشئ جدول قاعدة بيانات بهذا الشكل:
CREATE TABLE myapp_person (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
ملاحظات تقنية:
- اسم الجدول
myapp_person
يتم اشتقاقه تلقائيًا من بعض بيانات النموذج الوصفية، ولكن يمكن تعديله. راجع أسماء الجداول لمزيد من التفاصيل. - يتم إضافة حقل
id
تلقائيًا، ولكن يمكن تغيير هذا السلوك. راجع حقول المفتاح الأساسي التلقائية. - التنسيق الموجود في أمر
CREATE TABLE
في هذا المثال يستخدم بناء جملة PostgreSQL، ولكن من المهم ملاحظة أن Django يستخدم SQL مخصص يتناسب مع قاعدة البيانات المحددة في ملف الإعدادات الخاص بك.
استخدام النماذج (Using Models)
بمجرد تعريف النماذج الخاصة بك، تحتاج إلى إخبار Django أنك ستستخدم هذه النماذج. يتم ذلك عن طريق تعديل ملف الإعدادات (settings) وتغيير إعداد INSTALLED_APPS
لإضافة اسم الوحدة (module) التي تحتوي على الملف models.py
.
على سبيل المثال، إذا كانت النماذج الخاصة بتطبيقك موجودة في الوحدة myapp.models
(الهيكلية التي يتم إنشاؤها بواسطة السكربت manage.py startapp
)، يجب أن يتضمن إعداد INSTALLED_APPS
في ملف الإعدادات ما يلي:
INSTALLED_APPS = [
# ...
"myapp",
# ...
]
عند إضافة تطبيقات جديدة إلى INSTALLED_APPS
، تأكد من تشغيل الأمر manage.py migrate
، ويمكنك اختيارًا تشغيل الأمر manage.py makemigrations
أولاً لإنشاء الترحيلات (migrations) الخاصة بها.
الحقول (Fields)
أهم جزء في النموذج – والجزء الوحيد الذي يجب أن يكون موجودًا – هو قائمة الحقول التي يحددها في قاعدة البيانات. يتم تحديد الحقول كسمات (attributes) في الفئة (class). يجب أن تكون حذرًا في اختيار أسماء الحقول حتى لا تتعارض مع واجهة برمجة التطبيقات للنماذج (مثل clean
, save
, أو delete
).
مثال:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
في هذا المثال، تحتوي فئة Musician
على الحقول first_name
و last_name
و instrument
، بينما تحتوي فئة Album
على الحقل artist
الذي يرتبط بموديل Musician
باستخدام علاقة ForeignKey
، بالإضافة إلى الحقول الأخرى مثل name
, release_date
, و num_stars
.
أنواع الحقول (Field Types)
كل حقل في النموذج يجب أن يكون مثيلاً لإحدى فئات الحقول المناسبة في Django. تُستخدم أنواع فئات الحقول لتحديد العديد من الأمور:
- نوع العمود في قاعدة البيانات: يحدد نوع البيانات التي سيتم تخزينها (مثل
INTEGER
,VARCHAR
,TEXT
). - عنصر HTML الافتراضي: لتقديم حقل النموذج عند إنشاء النماذج (مثل
<input type="text">
,<select>
). - متطلبات التحقق الأساسية: التي تُستخدم في لوحة إدارة Django وفي النماذج التي يتم توليدها تلقائيًا.
يأتي Django مع العديد من أنواع الحقول الجاهزة؛ يمكنك العثور على القائمة الكاملة في مرجع حقول النماذج. إذا لم تكن الحقول المضمنة مناسبة، يمكنك بسهولة كتابة الحقول الخاصة بك؛ راجع كيفية إنشاء حقول نماذج مخصصة.
خيارات الحقول (Field Options)
تقبل كل حقل مجموعة من الوسائط (arguments) الخاصة به والمحددة لنوع الحقل. على سبيل المثال، حقل CharField
(وفئاته الفرعية) يتطلب الوسيطة max_length
التي تحدد حجم العمود من نوع VARCHAR
المستخدم لتخزين البيانات.
هناك أيضًا مجموعة من الوسائط المشتركة المتاحة لجميع أنواع الحقول. جميعها اختيارية، وتُشرح بالتفصيل في المرجع. فيما يلي ملخص سريع لأكثرها استخدامًا:
-
null
:- إذا كانت
True
، يقوم Django بتخزين القيم الفارغة كـNULL
في قاعدة البيانات. القيمة الافتراضية هيFalse
.
- إذا كانت
-
blank
:-
إذا كانت
True
، يُسمح بترك الحقل فارغًا. القيمة الافتراضية هيFalse
.
ملاحظة:
null
يتعلق بقاعدة البيانات فقط، بينماblank
يتعلق بالتحقق من صحة النموذج. إذا كان الحقل يحتوي علىblank=True
، فإن التحقق من صحة النموذج يسمح بإدخال قيمة فارغة. أما إذا كانblank=False
، فإن الحقل سيكون مطلوبًا. -
-
choices
:- تسلسل من الأزواج الثنائية (tuples)، أو خريطة (mapping)، أو نوع تعداد (enumeration type)، أو وظيفة قابلة للاستدعاء (callable) تُعيد أيًا من التنسيقات السابقة. يتم استخدامها كاختيارات لهذا الحقل. إذا تم توفير هذا الخيار، سيكون عنصر HTML الافتراضي مربع اختيار (
select box
) بدلاً من حقل نصي عادي، وسيتم تقييد الاختيارات بالقيم المقدمة.
- تسلسل من الأزواج الثنائية (tuples)، أو خريطة (mapping)، أو نوع تعداد (enumeration type)، أو وظيفة قابلة للاستدعاء (callable) تُعيد أيًا من التنسيقات السابقة. يتم استخدامها كاختيارات لهذا الحقل. إذا تم توفير هذا الخيار، سيكون عنصر HTML الافتراضي مربع اختيار (
مثال على قائمة اختيارات:
YEAR_IN_SCHOOL_CHOICES = [
("FR", "Freshman"),
("SO", "Sophomore"),
("JR", "Junior"),
("SR", "Senior"),
("GR", "Graduate"),
]
ملاحظة:
يتم إنشاء ترحيل (migration) جديد في كل مرة يتم فيها تغيير ترتيب القيم في قائمةchoices
.
تفاصيل إضافية عن choices
- العنصر الأول في كل زوج (
tuple
) يمثل القيمة التي سيتم تخزينها في قاعدة البيانات. - العنصر الثاني يمثل النص الذي سيتم عرضه في واجهة النموذج.
الوصول إلى القيم المعروضة (get_FOO_display
)
يمكنك استخدام الطريقة get_FOO_display()
لاسترجاع النص المعروض للحقل بناءً على القيمة المخزنة.
مثال:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = {
"S": "Small",
"M": "Medium",
"L": "Large",
}
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
استخدام:
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
استخدام تصنيفات (Enumerations) لتعريف الاختيارات
يمكنك استخدام فئات التعداد (enumeration classes) لتعريف الاختيارات بطريقة مختصرة ومنظمة.
مثال:
from django.db import models
class Runner(models.Model):
MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE")
name = models.CharField(max_length=60)
medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
النتيجة:
تعمل فئات التعداد على تقليل التعقيد عند تعريف اختيارات ثابتة، مما يجعل الكود أكثر وضوحًا وسهولة في الصيانة.
تغيير جديد في Django 5.0:
- تم إضافة دعم الخرائط (mappings) والدوال القابلة للاستدعاء (callables) لتحديد الخيارات.
يمكنك الآن استخدام طرق أكثر مرونة لإنشاء قائمة الخيارات الخاصة بك.
للحصول على أمثلة إضافية:
راجع مرجع حقول النماذج.
خيارات الحقول الشائعة (Common Field Options)
1. default
- الوصف:
القيمة الافتراضية للحقل. يمكن أن تكون قيمة ثابتة أو دالة قابلة للاستدعاء (callable
). إذا كانت دالة، يتم استدعاؤها في كل مرة يتم فيها إنشاء كائن جديد. - مثال:
from django.db import models
from datetime import datetime
class Event(models.Model):
created_at = models.DateTimeField(default=datetime.now)
2. db_default
- الوصف:
القيمة الافتراضية التي يتم تحديدها على مستوى قاعدة البيانات. يمكن أن تكون قيمة حرفية أو وظيفة قاعدة بيانات (مثلCURRENT_TIMESTAMP
).- إذا تم تعيين كل من
db_default
وdefault
، فسيتم استخدامdefault
عند إنشاء الكائنات عبر ORM. - سيتم تطبيق
db_default
عند إدراج الصفوف خارج ORM أو عند إضافة حقل جديد في الترحيلات.
- إذا تم تعيين كل من
3. help_text
- الوصف:
نص إضافي يظهر مع عنصر النموذج في الواجهة. يُعتبر مفيدًا لتوثيق الحقول، حتى إذا لم يتم استخدام الحقل في النماذج. - مثال:
class Book(models.Model):
title = models.CharField(max_length=100, help_text="أدخل عنوان الكتاب هنا.")
4. primary_key
-
الوصف:
إذا كانتTrue
، يكون الحقل هو المفتاح الأساسي للنموذج.
إذا لم يتم تحديدprimary_key=True
لأي حقل، يضيف Django تلقائيًا حقلًا من النوعIntegerField
كمفتاح أساسي. -
ملاحظات:
- الحقل الأساسي يكون للقراءة فقط.
- إذا تم تعديل قيمته في كائن موجود وتم حفظ الكائن، سيتم إنشاء كائن جديد.
-
مثال:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=100, primary_key=True)
نتيجة:
>>> fruit = Fruit.objects.create(name="Apple")
>>> fruit.name = "Pear"
>>> fruit.save()
>>> Fruit.objects.values_list("name", flat=True)
<QuerySet ['Apple', 'Pear']>
5. unique
- الوصف:
إذا كانتTrue
، يجب أن يكون الحقل فريدًا عبر الجدول. - مثال:
class User(models.Model):
email = models.EmailField(unique=True)
الحقول الأساسية التلقائية (Automatic Primary Key Fields)
- بشكل افتراضي، يضيف Django لكل نموذج حقل مفتاح أساسي تلقائي (
id
) من النوع المحدد فيAppConfig.default_auto_field
أو في الإعدادDEFAULT_AUTO_FIELD
. - مثال:
id = models.BigAutoField(primary_key=True)
- إذا كنت تريد تخصيص الحقل الأساسي، قم بتعيين
primary_key=True
على أحد الحقول. - ملاحظة:
يجب أن يحتوي كل نموذج على حقل واحد فقط كـprimary_key
(سواء تم تعريفه صراحةً أو تمت إضافته تلقائيًا).
أسماء الحقول الوصفية (Verbose Field Names)
الوصف:
- كل نوع من الحقول (باستثناء
ForeignKey
,ManyToManyField
, وOneToOneField
) يقبل وسيطًا اختياريًا كأول معطى لتحديد اسم وصفي (verbose name). - إذا لم يتم تقديم اسم وصفي، يقوم Django تلقائيًا بإنشائه باستخدام اسم السمة الخاصة بالحقل، مع تحويل الشرطات السفلية (
_
) إلى مسافات.
أمثلة على الأسماء الوصفية:
- اسم وصفي مخصص:
first_name = models.CharField("person's first name", max_length=30)
- اسم وصفي افتراضي:
first_name = models.CharField(max_length=30)
# يتم إنشاء الاسم الوصفي افتراضيًا كـ "first name"
الحقول التي تتطلب اسم النموذج كمعطى أولي:
بالنسبة للحقول التي تعتمد على علاقات (ForeignKey
, ManyToManyField
, و OneToOneField
):
- يتم استخدام المعامل
verbose_name
ككلمة مفتاحية لتحديد الاسم الوصفي.
أمثلة:
poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="related place",
)
ملاحظات:
- عدم كتابة الأحرف الكبيرة:
يُفضل عدم كتابة الحرف الأول من الاسم الوصفي بحرف كبير. يقوم Django تلقائيًا بتكبير أول حرف عند الحاجة، مثلًا في عناوين الأعمدة أو النماذج الإدارية.
العلاقات في Django (Relationships)
العلاقات بين الجداول هي جوهر قوة قواعد البيانات العلائقية. يتيح Django طرقًا لتحديد الأنواع الثلاثة الأكثر شيوعًا من العلاقات:
- العلاقات "العديد إلى واحد" (Many-to-One).
- العلاقات "العديد إلى العديد" (Many-to-Many).
- العلاقات "واحد إلى واحد" (One-to-One).
1. العلاقات "العديد إلى واحد" (Many-to-One Relationships)
الوصف:
- تُستخدم لإنشاء علاقة حيث يمكن لجدول (نموذج) واحد أن يحتوي على العديد من السجلات المرتبطة بسجل واحد في جدول آخر.
- يتم تعريف هذه العلاقة باستخدام
ForeignKey
.
المتطلبات:
- يتطلب الحقل
ForeignKey
معطًى إجباريًا: اسم النموذج المرتبط.
مثال:
في هذا المثال، يحتوي نموذج Car
على علاقة مع نموذج Manufacturer
:
from django.db import models
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
model = models.CharField(max_length=100)
تفاصيل إضافية:
-
علاقات تكرارية (Recursive Relationships):
- يمكن أن تكون العلاقة بين كائن ونفسه:
class Employee(models.Model): manager = models.ForeignKey( "self", on_delete=models.SET_NULL, null=True, blank=True, )
- يمكن أن تكون العلاقة بين كائن ونفسه:
-
علاقات مع نماذج غير معرّفة بعد:
- يمكن الإشارة إلى نموذج غير مُعرف بعد باستخدام سلسلة نصية:
class Car(models.Model): manufacturer = models.ForeignKey("Manufacturer", on_delete=models.CASCADE)
- يمكن الإشارة إلى نموذج غير مُعرف بعد باستخدام سلسلة نصية:
-
اختيار اسم الحقل:
- يوصى، ولكن ليس إلزاميًا، بتسمية الحقل باسم النموذج المرتبط بحروف صغيرة (
manufacturer
في المثال أعلاه). ومع ذلك، يمكن تسميته بأي شيء:class Car(models.Model): company_that_makes_it = models.ForeignKey( Manufacturer, on_delete=models.CASCADE, )
- يوصى، ولكن ليس إلزاميًا، بتسمية الحقل باسم النموذج المرتبط بحروف صغيرة (
ملاحظات إضافية:
- يتضمن
ForeignKey
خيارات إضافية (مثلon_delete
) للتحكم في كيفية عمل العلاقة، وهي موضحة في المرجع الخاص بحقول النماذج.
2. العلاقات "العديد إلى العديد" (Many-to-Many Relationships)
الوصف:
- تُستخدم لإنشاء علاقة حيث يمكن لكل سجل في جدول أن يرتبط بعدد غير محدد من السجلات في جدول آخر، والعكس صحيح.
- يتم تعريف هذه العلاقة باستخدام
ManyToManyField
.
مثال:
في هذا المثال، يمكن لكل كاتب تأليف عدة كتب، ويمكن لكل كتاب أن يكون له عدة كُتّاب:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
authors = models.ManyToManyField(Author)
التعامل مع العلاقات:
- عند تعريف حقل
ManyToManyField
، يتم إنشاء جدول وسيط (junction table) تلقائيًا لتخزين العلاقات بين الجداول.
خيارات إضافية:
- مثل
related_name
وthrough
، وهي موضحة في المرجع الخاص بحقول النماذج.
3. العلاقات "واحد إلى واحد" (One-to-One Relationships)
الوصف:
- تُستخدم لإنشاء علاقة حيث يمكن لكل سجل في جدول أن يرتبط بسجل واحد فقط في جدول آخر، والعكس صحيح.
- يتم تعريف هذه العلاقة باستخدام
OneToOneField
.
مثال:
في هذا المثال، يحتوي نموذج Place
على علاقة "واحد إلى واحد" مع نموذج Restaurant
:
class Place(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=200)
class Restaurant(models.Model):
place = models.OneToOneField(Place, on_delete=models.CASCADE)
serves_pizza = models.BooleanField(default=False)
العلاقات "العديد إلى العديد" باستخدام ManyToManyField
الوصف:
ManyToManyField
تُستخدم لإنشاء علاقة حيث يمكن لكل سجل في نموذج أن يرتبط بعدد غير محدد من السجلات في نموذج آخر، والعكس صحيح.
المتطلبات:
- يتطلب
ManyToManyField
معطى إجباريًا: اسم النموذج المرتبط.
مثال على الاستخدام:
إذا كان نموذج Pizza
يمكن أن يحتوي على عدة Topping
(تغطيات)، وكان كل Topping
يمكن أن يكون مرتبطًا بعدة Pizza
:
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=50)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
التفاصيل:
- في المثال أعلاه، يتم تعريف العلاقة "العديد إلى العديد" في نموذج
Pizza
باستخدام الحقلtoppings
. - يُفضل تسمية الحقل باستخدام صيغة الجمع (مثل
toppings
) لوصف مجموعة الكائنات المرتبطة.
العلاقات العكسية (Recursive Relationships):
يمكنك تعريف علاقة "عديد إلى عديد" بين كائن ونفسه. على سبيل المثال:
class Person(models.Model):
name = models.CharField(max_length=50)
friends = models.ManyToManyField("self", blank=True)
- في المثال أعلاه، لكل شخص مجموعة من الأصدقاء، ويمكن أن يكون الشخص صديقًا لنفسه أو لأشخاص آخرين.
علاقات مع نماذج غير معرّفة بعد:
يمكنك الإشارة إلى نموذج لم يتم تعريفه بعد باستخدام سلسلة نصية تحتوي على اسم النموذج:
class Pizza(models.Model):
toppings = models.ManyToManyField("Topping")
class Topping(models.Model):
name = models.CharField(max_length=50)
أفضل الممارسات:
-
تحديد مكان العلاقة:
- لا ينبغي أن تضيف
ManyToManyField
في كلا النموذجين المرتبطين. يكفي أن تكون العلاقة موجودة في أحدهما فقط. - في المثال أعلاه، يُفضل وضع العلاقة في نموذج
Pizza
، حيث من الطبيعي أن يكون المستخدم قادرًا على إضافة تغطيات إلى البيتزا عند تحرير نموذجها.
- لا ينبغي أن تضيف
-
تخصيص العلاقة:
- يوفر
ManyToManyField
خيارات إضافية مثل:related_name
: لتحديد اسم الوصول العكسي.through
: لتحديد جدول وسيط مخصص.
- يوفر
التحكم في العلاقة من خلال النموذج:
- عند تعريف العلاقة، يتم إنشاء جدول وسيط (junction table) تلقائيًا لإدارة العلاقة.
- يمكن أيضًا تخصيص الجدول الوسيط باستخدام خيار
through
إذا كنت بحاجة إلى إضافة معلومات إضافية حول العلاقة.
مثال على جدول وسيط مخصص:
إذا كنت بحاجة إلى تخزين تفاصيل مثل الكمية عند ربط بيتزا وتغطية:
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField("Topping", through="PizzaTopping")
class Topping(models.Model):
name = models.CharField(max_length=50)
class PizzaTopping(models.Model):
pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
topping = models.ForeignKey(Topping, on_delete=models.CASCADE)
quantity = models.IntegerField()
خيارات إضافية:
- يمكن الاطلاع على قائمة الخيارات الكاملة لـ
ManyToManyField
في المرجع الخاص بحقول النماذج.
إضافة حقول إضافية على العلاقات "العديد إلى العديد"
في بعض الحالات، تحتاج إلى تخزين معلومات إضافية حول العلاقة "العديد إلى العديد". على سبيل المثال، قد ترغب في تتبع تاريخ انضمام شخص إلى مجموعة موسيقية أو سبب دعوته للانضمام.
تعريف العلاقة باستخدام نموذج وسيط
بدلاً من استخدام ManyToManyField
مباشرةً، يمكنك تعريف نموذج وسيط يحتوي على الحقول الإضافية. يتم ربط هذا النموذج بالـ ManyToManyField
باستخدام الوسيط through
.
مثال:
لنفترض أننا نريد تتبع العضويات بين الأشخاص والمجموعات الموسيقية:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through="Membership")
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
تفسير الكود:
-
النموذج الأساسي:
Person
يمثل الأشخاص.Group
يمثل المجموعات.
-
العلاقة "العديد إلى العديد":
- تم إنشاء العلاقة بين الأشخاص والمجموعات باستخدام الحقل
members
في نموذجGroup
. - العلاقة تشير إلى النموذج الوسيط
Membership
من خلال الوسيطthrough="Membership"
.
- تم إنشاء العلاقة بين الأشخاص والمجموعات باستخدام الحقل
-
النموذج الوسيط (
Membership
):- يحتوي على الحقول الإضافية
date_joined
وinvite_reason
. - يربط بين الأشخاص والمجموعات باستخدام الحقول:
person
(يشير إلى النموذجPerson
).group
(يشير إلى النموذجGroup
).
- يحتوي على الحقول الإضافية
قيود النموذج الوسيط:
-
يجب أن يحتوي النموذج الوسيط على مفتاحين أجنبيين فقط:
- مفتاح أجنبي إلى النموذج المصدر (مثل
Group
). - مفتاح أجنبي إلى النموذج الهدف (مثل
Person
).
- مفتاح أجنبي إلى النموذج المصدر (مثل
-
إذا كان هناك أكثر من مفتاح أجنبي، يجب تحديد الحقول باستخدام
through_fields
لتجنب أخطاء التحقق. -
العلاقات الذاتية: يمكن أن يحتوي النموذج الوسيط على مفتاحين أجنبيين يشيران إلى نفس النموذج (علاقة ذاتية)، ولكن يجب تحديد الحقول بوضوح باستخدام
through_fields
عند الضرورة.
إنشاء العلاقات في قاعدة البيانات:
يمكنك إنشاء العلاقات باستخدام النموذج الوسيط مباشرةً:
from datetime import date
# إنشاء الأشخاص والمجموعة
ringo = Person.objects.create(name="Ringo Starr")
paul = Person.objects.create(name="Paul McCartney")
beatles = Group.objects.create(name="The Beatles")
# إضافة العلاقة باستخدام النموذج الوسيط
Membership.objects.create(
person=ringo,
group=beatles,
date_joined=date(1962, 8, 16),
invite_reason="Needed a new drummer."
)
Membership.objects.create(
person=paul,
group=beatles,
date_joined=date(1960, 8, 1),
invite_reason="Wanted to form a band."
)
# الوصول إلى العلاقات
print(beatles.members.all()) # جميع أعضاء المجموعة
print(ringo.group_set.all()) # جميع المجموعات التي ينتمي إليها الشخص
فوائد استخدام نموذج وسيط:
- إمكانية تخزين بيانات إضافية حول العلاقة.
- توفير مرونة أكبر في إدارة العلاقات المعقدة.
- سهولة استعلام البيانات المرتبطة بالعلاقة.
إدارة العلاقات "العديد إلى العديد" باستخدام نموذج وسيط
في Django، يمكنك إدارة العلاقات "العديد إلى العديد" باستخدام الطرق المدمجة مثل add()
، create()
، و set()
، حتى عند استخدام نموذج وسيط يحتوي على حقول إضافية.
إضافة علاقات باستخدام add()
و create()
و set()
1. إضافة علاقة باستخدام add()
يمكنك استخدام add()
لإنشاء علاقة بين الكائنات مع تحديد القيم الافتراضية للحقول الإضافية باستخدام through_defaults
:
from datetime import date
beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
2. إنشاء علاقة وكائن جديد باستخدام create()
beatles.members.create(
name="George Harrison",
through_defaults={"date_joined": date(1960, 8, 1)}
)
3. تعيين عدة علاقات باستخدام set()
beatles.members.set(
[john, paul, ringo, george],
through_defaults={"date_joined": date(1960, 8, 1)}
)
ملاحظة: في بعض الحالات، قد يكون من الأفضل إنشاء كائنات النموذج الوسيط مباشرةً إذا كنت تحتاج إلى تحكم أكبر.
حذف العلاقات
1. إزالة كائن معين باستخدام remove()
إذا كانت الجدول الوسيط يسمح بوجود نسخ متعددة للعلاقة بين كائنين (مثل (person, group)
)، فإن remove()
ستزيل جميع النسخ:
# إضافة علاقة مكررة
Membership.objects.create(
person=ringo,
group=beatles,
date_joined=date(1968, 9, 4),
invite_reason="You've been gone for a month and we miss you.",
)
# حذف جميع النسخ المرتبطة بـ Ringo Starr
beatles.members.remove(ringo)
2. إزالة جميع العلاقات باستخدام clear()
# إزالة جميع أعضاء الفرقة
beatles.members.clear()
# حذف جميع سجلات النموذج الوسيط
Membership.objects.all() # QuerySet []
الاستعلام عن العلاقات
1. الاستعلام باستخدام الحقول المرتبطة بالعلاقة
يمكنك استعلام العلاقات "العديد إلى العديد" بالطريقة المعتادة:
# العثور على المجموعات التي تحتوي على عضو يبدأ اسمه بـ "Paul"
Group.objects.filter(members__name__startswith="Paul")
2. الاستعلام باستخدام حقول النموذج الوسيط
ميزة النموذج الوسيط هي إمكانية استعلام الحقول الإضافية:
# العثور على أعضاء فرقة Beatles الذين انضموا بعد 1 يناير 1961
Person.objects.filter(
group__name="The Beatles",
membership__date_joined__gt=date(1961, 1, 1)
)
الوصول إلى معلومات العلاقة الوسيطة
1. عبر النموذج الوسيط مباشرةً
يمكنك استعلام المعلومات عن طريق استعلام النموذج الوسيط:
ringos_membership = Membership.objects.get(group=beatles, person=ringo)
print(ringos_membership.date_joined) # 1962-08-16
print(ringos_membership.invite_reason) # Needed a new drummer.
2. عبر العلاقة العكسية من الكائن
يمكنك استعلام البيانات الوسيطة باستخدام العلاقة العكسية:
ringos_membership = ringo.membership_set.get(group=beatles)
print(ringos_membership.date_joined) # 1962-08-16
print(ringos_membership.invite_reason) # Needed a new drummer.
العلاقات "واحد إلى واحد" في Django
تعريف العلاقة
لتعريف علاقة "واحد إلى واحد"، نستخدم الحقل OneToOneField
كأي نوع آخر من الحقول في النموذج، مع تضمينه كخاصية داخلية للنموذج.
استخدام OneToOneField
1. متى نستخدمه؟
تُستخدم علاقة "واحد إلى واحد" بشكل شائع عند وجود كائن "يمتد" (extends) من كائن آخر.
على سبيل المثال:
- إذا كان لديك قاعدة بيانات للأماكن (Places) تحتوي على حقول مثل العنوان ورقم الهاتف.
- ثم أردت إضافة قاعدة بيانات للمطاعم (Restaurants)، يمكن جعل
Restaurant
يحتوي على حقلOneToOneField
للإشارة إلىPlace
.
2. المتطلبات
يتطلب OneToOneField
توفير اسم النموذج الذي سيتم الارتباط به كوسيطة أولى.
مثال على العلاقة
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=255)
def __str__(self):
return self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place, on_delete=models.CASCADE)
serves_pizza = models.BooleanField(default=False)
serves_pasta = models.BooleanField(default=False)
def __str__(self):
return f"Restaurant at {self.place.name}"
الوصف
- النموذج
Place
يحتوي على معلومات عامة. - النموذج
Restaurant
يحتوي على تفاصيل إضافية ولكنه يعتمد علىPlace
.
ميزات إضافية لـ OneToOneField
1. العلاقات العودية (Recursive Relationships)
يمكن أن تشير العلاقة "واحد إلى واحد" إلى نفسها.
2. المراجع للنماذج غير المعرفة بعد
يمكنك إنشاء علاقات مع نماذج لم يتم تعريفها بعد باستخدام سلسلة نصية تشير إلى اسم النموذج:
class Employee(models.Model):
manager = models.OneToOneField("self", on_delete=models.CASCADE, null=True, blank=True)
ملاحظات هامة
-
الحقل الأساسي (Primary Key):
- لم يعد حقل
OneToOneField
يتم تعيينه تلقائيًا كحقل أساسي للنموذج. - يمكنك جعله حقلًا أساسيًا يدويًا باستخدام
primary_key=True
إذا كنت بحاجة لذلك. - من الممكن الآن أن يحتوي نموذج واحد على عدة حقول
OneToOneField
.
- لم يعد حقل
-
المعلمة
parent_link
:- يمكن استخدام
parent_link=True
عند استخدام الوراثة لتحديد العلاقة الأساسية بين النماذج.
- يمكن استخدام
مثال عملي
إضافة مطعم ومكان
place = Place.objects.create(name="Central Park Cafe", address="123 Central Park")
restaurant = Restaurant.objects.create(place=place, serves_pizza=True, serves_pasta=False)
print(restaurant) # Restaurant at Central Park Cafe
الوصول إلى البيانات المرتبطة
- من الكائن الأب:
place_restaurant = place.restaurant
print(place_restaurant.serves_pizza) # True
- من الكائن التابع:
restaurant_place = restaurant.place
print(restaurant_place.name) # Central Park Cafe
النماذج عبر ملفات متعددة
1. الربط بين النماذج من تطبيقات مختلفة
يمكنك بسهولة إنشاء علاقة بين نموذج في تطبيق معين ونموذج آخر في تطبيق مختلف. للقيام بذلك:
- قم باستيراد النموذج الآخر في أعلى الملف الذي يحتوي على النموذج الخاص بك.
- استخدم النموذج المستورد حيثما تحتاج.
مثال:
from django.db import models
from geography.models import ZipCode # استيراد النموذج من تطبيق آخر
class Restaurant(models.Model):
# تعريف العلاقة مع النموذج الآخر
zip_code = models.ForeignKey(
ZipCode, # النموذج المستورد
on_delete=models.SET_NULL, # التعامل مع الحذف
blank=True,
null=True,
)
قيود على أسماء الحقول
1. كلمات محجوزة في Python
لا يمكن استخدام الكلمات المحجوزة في Python كأسماء للحقول في النماذج، لأنها تسبب أخطاء في البنية (SyntaxError).
مثال على كلمة محجوزة:
class Example(models.Model):
pass = models.IntegerField() # هذا غير مسموح لأن "pass" كلمة محجوزة!
2. الحقول التي تحتوي على أكثر من شرطة سفلية متتالية
لا يمكن أن يحتوي اسم الحقل على أكثر من شرطة سفلية متتالية (__
) بسبب تعارض مع نظام البحث (lookup) الخاص بـ Django.
مثال غير صحيح:
class Example(models.Model):
foo__bar = models.IntegerField() # يحتوي على شرطتين سفليتين!
3. أسماء الحقول التي تنتهي بشرطة سفلية
لا يمكن أن ينتهي اسم الحقل بشرطة سفلية (_
) بسبب تعارض مع بنية Django.
العمل حول القيود
1. استخدام db_column
يمكنك تجاوز القيود على أسماء الحقول باستخدام الخيار db_column
لتحديد اسم العمود في قاعدة البيانات بشكل مختلف عن اسم الحقل في النموذج.
مثال:
class Example(models.Model):
my_field = models.IntegerField(db_column='my_custom_field_name')
الكلمات المحجوزة في SQL
- مسموح باستخدام الكلمات المحجوزة في SQL (مثل
join
أوselect
) كأسماء للحقول. - Django يقتبس أسماء الحقول وأسماء الجداول تلقائيًا في استعلامات SQL لضمان عدم حدوث تعارض.
مثال:
class Example(models.Model):
select = models.CharField(max_length=50) # هذا مسموح لأن Django يعالج الاقتباس.
إنشاء أنواع حقول مخصصة
إذا كانت الحقول المضمنة في Django لا تناسب احتياجاتك، أو إذا كنت ترغب في استخدام نوع بيانات أقل شيوعًا في قاعدة البيانات، يمكنك إنشاء نوع الحقل الخاص بك.
مثال:
- تحتاج إلى إنشاء نوع حقل مخصص يدعم منطقًا محددًا أو تنسيق بيانات خاص.
خيارات Meta في النماذج
1. تعريف البيانات الوصفية للنموذج باستخدام Meta
يمكنك استخدام فئة Meta
داخل النموذج لإعطاء معلومات إضافية حول النموذج مثل ترتيب السجلات، أو أسماء الجداول في قاعدة البيانات، أو أسماء البشر المفسرة بصيغة مفردة وجمع.
مثال:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"] # تحديد ترتيب السجلات
verbose_name_plural = "oxen" # تحديد الاسم الجمعي للنموذج
خيارات Meta المشتركة:
ordering
: تحديد ترتيب السجلات عند الاستعلام.verbose_name
: تحديد الاسم المفرد للنموذج.verbose_name_plural
: تحديد الاسم الجمعي للنموذج.db_table
: تحديد اسم الجدول في قاعدة البيانات.unique_together
وindex_together
: لتحديد مجموعات فريدة أو فهرسة الحقول معًا.
السمات الخاصة بالنموذج
1. مدير النموذج (Manager)
يعد objects
هو المدير الافتراضي للنموذج. يعمل المدير كواجهة لعمليات الاستعلام مع قاعدة البيانات ويتيح لك استرجاع الكائنات من قاعدة البيانات. إذا لم تقم بتعريف مدير مخصص، فإن objects
سيكون هو اسم المدير.
مثال:
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
# استرجاع جميع الأشخاص
people = Person.objects.all()
2. طرق النموذج (Model Methods)
يمكنك تعريف طرق مخصصة داخل النموذج لإضافة وظائف على مستوى السجل (الصف). هذه الطرق تسمح لك بتنفيذ منطق العمل (business logic) مباشرة داخل النموذج.
مثال:
from django.db import models
import datetime
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"يحدد حالة الشخص فيما إذا كان من جيل البيبي بومر."
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"إرجاع الاسم الكامل للشخص."
return f"{self.first_name} {self.last_name}"
المثيل (Property)
يمكنك استخدام خاصية @property
لتحويل طريقة إلى خاصية يمكن الوصول إليها مثل الحقل دون الحاجة لاستخدام الأقواس.
3. طرق Python السحرية
يجب أن تعرف بعض الطرق المهمة مثل __str__()
و get_absolute_url()
.
-
__str__()
: تستخدم هذه الطريقة لتحويل الكائن إلى تمثيل نصي. وهي مفيدة عند عرض الكائنات في واجهة المستخدم أو في واجهة الإدارة.مثال:
def __str__(self): return f"{self.first_name} {self.last_name}"
-
get_absolute_url()
: تستخدم لتحديد URL فريد للكائن. يجب أن تُستخدم هذه الطريقة عندما يحتوي الكائن على عنوان URL فريد.مثال:
def get_absolute_url(self): return f"/people/{self.id}/"
تجاوز الطرق المحددة مسبقًا في النماذج
يمكنك تعديل بعض الطرق المحددة مسبقًا في النماذج مثل save()
و delete()
لتخصيص سلوك قاعدة البيانات. في بعض الحالات، قد ترغب في تنفيذ إجراءات معينة عندما تقوم بحفظ كائن أو حذفه.
1. تجاوز طريقة save()
تُستخدم طريقة save()
لحفظ الكائن في قاعدة البيانات. يمكنك تجاوز هذه الطريقة لتنفيذ بعض الإجراءات قبل أو بعد حفظ الكائن. على سبيل المثال، قد ترغب في تعديل حقل أو إجراء عملية معينة قبل حفظ الكائن.
مثال لتجاوز طريقة save()
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, **kwargs):
do_something() # تنفيذ شيء قبل الحفظ
super().save(**kwargs) # استدعاء طريقة save الأصلية
do_something_else() # تنفيذ شيء بعد الحفظ
مثال لمنع الحفظ تحت شرط معين
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, **kwargs):
if self.name == "Yoko Ono's blog":
return # منع الحفظ إذا كان الاسم هو "مدونة يوكو أونو"
else:
super().save(**kwargs) # استدعاء طريقة save الأصلية
ملاحظة: من الضروري دائمًا استدعاء الطريقة الأصلية super().save(**kwargs)
لكي يتم حفظ الكائن في قاعدة البيانات. إذا نسيت استدعاء الطريقة الأصلية، لن يحدث الحفظ ولن يتم تعديل قاعدة البيانات.
2. تحديث الحقول في طريقة save()
إذا كنت ترغب في تحديث حقل معين داخل طريقة save()
، فقد ترغب أيضًا في إضافة هذا الحقل إلى قائمة update_fields
. هذا يضمن أن الحقل سيتم حفظه عند تحديد الحقول التي سيتم تحديثها.
مثال لتحديث حقل داخل save()
from django.db import models
from django.utils.text import slugify
class Blog(models.Model):
name = models.CharField(max_length=100)
slug = models.TextField()
def save(self, **kwargs):
self.slug = slugify(self.name) # تحديث حقل slug بناءً على حقل name
if (
update_fields := kwargs.get("update_fields")
) is not None and "name" in update_fields:
kwargs["update_fields"] = {"slug"}.union(update_fields)
super().save(**kwargs) # استدعاء طريقة save الأصلية
3. تجاوز طريقة delete()
على غرار طريقة save()
, يمكنك أيضًا تجاوز طريقة delete()
لتنفيذ منطق مخصص عند حذف الكائنات. لكن يجب أن تعرف أن طريقة delete()
لا تُنفذ دائمًا في العمليات الكبيرة (bulk operations) أو عند الحذف التلقائي بسبب القيود المرتبطة بالقيد الأجنبي.
مثال لتجاوز delete()
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def delete(self, *args, **kwargs):
# تنفيذ شيء قبل الحذف
do_something_before_deleting()
super().delete(*args, **kwargs) # استدعاء طريقة delete الأصلية
4. العمليات الكبيرة (Bulk Operations)
ملاحظة مهمة: عند إجراء عمليات كبيرة مثل الحفظ أو التحديث في كتلة (bulk operations)، فإن الطرق مثل save()
و إشارات pre_save
و post_save
لن تُنفذ. لذا، إذا كنت بحاجة إلى تنفيذ منطق مخصص أثناء العمليات الكبيرة، يجب أن تستخدم إشارات Django مثل pre_save
و post_save
أو إشارات الحذف مثل pre_delete
و post_delete
.
تنفيذ SQL مخصص في Django
إحدى الأنماط الشائعة هي كتابة استعلامات SQL مخصصة داخل طرق النماذج أو في طرق على مستوى الوحدة (module-level methods). يمكن استخدام SQL الخام (raw SQL) لتنفيذ الاستعلامات مباشرة على قاعدة البيانات في Django، مما يسمح لك بتنفيذ عمليات معقدة لا تدعمها واجهات Django ORM.
مثال على استخدام SQL الخام
from django.db import connection
def my_custom_sql():
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM my_table WHERE my_field = %s", ['value'])
rows = cursor.fetchall()
return rows
في هذا المثال، يتم استخدام connection.cursor()
لإنشاء محرك قاعدة بيانات مخصص وتنفيذ استعلام SQL. بعد ذلك، يتم جلب النتائج باستخدام fetchall()
.
لمزيد من التفاصيل حول كيفية استخدام SQL الخام في Django، يمكنك الرجوع إلى الوثائق الخاصة بـ raw SQL.
وراثة النماذج في Django
تعمل وراثة النماذج في Django بشكل مشابه تمامًا للوراثة في Python، لكن من المهم أن تتبع الأساسيات. يجب أن تكون فئة النموذج الأساسية فرعية من django.db.models.Model
.
هناك ثلاثة أنواع من الوراثة التي يمكنك استخدامها في Django:
-
الفئات الأساسية المجردة (Abstract Base Classes): هذه تستخدم عندما ترغب في إدخال معلومات مشتركة في العديد من النماذج الأخرى. الفئة الأساسية لن تستخدم أبداً لإنشاء جدول قاعدة بيانات، بل سيتم إضافة الحقول الخاصة بها إلى الفئات الفرعية.
-
الوراثة متعددة الجداول (Multi-table Inheritance): هذه تستخدم عندما تريد لكل نموذج من النماذج الفرعية أن يكون له جدول قاعدة بيانات خاص به.
-
النماذج الوكيلة (Proxy Models): تستخدم عندما ترغب في تعديل سلوك Python فقط دون تغيير الحقول في النموذج.
الفئات الأساسية المجردة (Abstract Base Classes)
تعتبر الفئات الأساسية المجردة مفيدة عندما تريد وضع معلومات مشتركة في عدد من النماذج الأخرى. تكتب الفئة الأساسية مع تعيين abstract=True
في الفئة Meta
. هذا يعني أن Django لن يقوم بإنشاء جدول قاعدة بيانات لهذه الفئة الأساسية، بل سيضيف الحقول الخاصة بها إلى النماذج الفرعية.
مثال على الفئات الأساسية المجردة
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
في هذا المثال:
CommonInfo
هي فئة أساسية مجردة تحتوي على الحقول المشتركة مثلname
وage
.Student
هي فئة فرعية منCommonInfo
وتضيف حقلًا جديدًاhome_group
.
خصائص الفئات الأساسية المجردة
- لا يتم إنشاء جدول قاعدة بيانات للفئة الأساسية المجردة: بما أن
CommonInfo
مجردة، لا يتم إنشاء جدول قاعدة بيانات لها. - الحقول المشتركة تُضاف إلى النماذج الفرعية: يتم إضافة الحقول من
CommonInfo
إلى فئةStudent
، بحيث يصبح لدىStudent
الحقولname
وage
بالإضافة إلىhome_group
. - لا يمكن استخدام الفئة الأساسية المجردة مباشرة: لا يمكن إنشاء كائنات مباشرة من الفئة الأساسية المجردة.
تخصيص الحقول الموروثة
- يمكنك تخصيص الحقول الموروثة من الفئات الأساسية المجردة، مثل تغيير اسم الحقل أو إضافة قيمة جديدة.
- يمكن إزالة الحقول باستخدام
None
أو عن طريق استبدالها بحقل آخر في الفئة الفرعية.
وراثة الفئة Meta في Django
عند إنشاء فئة أساسية مجردة (Abstract Base Class)، يقوم Django بتوفير أي فئة Meta
تم تعريفها في الفئة الأساسية كخاصية (attribute). إذا لم تقم الفئة الفرعية بتعريف فئة Meta
خاصة بها، فإنها ستورث فئة Meta
الخاصة بالفئة الأساسية. وإذا كانت الفئة الفرعية ترغب في توسيع فئة Meta
الخاصة بالفئة الأساسية، يمكنها أن تقوم بتوريثها. إليك مثالًا على ذلك:
مثال على وراثة Meta من فئة أساسية مجردة
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ["name"]
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta):
db_table = "student_info"
في هذا المثال:
CommonInfo
هي فئة أساسية مجردة تحتوي على الحقول المشتركة مثلname
وage
.Student
هي فئة فرعية منCommonInfo
وتضيف حقلًا جديدًاhome_group
.- فئة
Meta
فيStudent
ترثMeta
الخاصة بـCommonInfo
وتضيف خصائص إضافية، مثلdb_table
.
إجراءات تخصيص Meta في الفئة الفرعية
عند تعريف فئة Meta
في الفئة الأساسية المجردة، قد ترغب في تخصيص بعض الخيارات في الفئة الفرعية:
abstract=False
: Django يقوم بتعديل فئةMeta
الخاصة بالفئة الأساسية المجردة عن طريق تعيينabstract=False
، مما يعني أن الفئات الفرعية لا تصبح فئات مجردة تلقائيًا. إذا كنت ترغب في جعل فئة فرعية مجردة أيضًا، يجب عليك تعيينabstract=True
فيMeta
الخاصة بها.- تخصيص
db_table
: لا ينبغي تضمينdb_table
في فئةMeta
الخاصة بالفئة الأساسية المجردة، حيث قد يؤدي ذلك إلى استخدام نفس جدول قاعدة البيانات لجميع الفئات الفرعية، مما لا يكون مرغوبًا عادة.
وراثة خيارات Meta من فئات متعددة
في حال كانت الفئة الفرعية ترث من فئات أساسية مجرّدة متعددة، فإن الخيارات من فئة Meta
الأولى فقط سيتم وراثتها بشكل افتراضي. ولإرث خيارات Meta
من جميع الفئات الأساسية، يجب أن تحدد وراثة فئة Meta
من جميع الفئات.
مثال على وراثة Meta من فئات متعددة
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ["name"]
class Unmanaged(models.Model):
class Meta:
abstract = True
managed = False
class Student(CommonInfo, Unmanaged):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta, Unmanaged.Meta):
pass
في هذا المثال، Student
ترث من فئتين أساسيتين CommonInfo
و Unmanaged
. ووراثة فئة Meta
تشمل الخيارات من كلا الفئتين الأساسيتين عبر دمج Meta
لكل منهما في فئة Meta
الخاصة بـ Student
.
الحذر عند استخدام related_name
و related_query_name
إذا كنت تستخدم related_name
أو related_query_name
في ForeignKey
أو ManyToManyField
في فئة أساسية مجردة، يجب عليك دائمًا تحديد اسم عكسي (reverse name) واسم استعلام عكسي (reverse query name) فريد لكل حقل. وذلك لأن الحقول في الفئة الأساسية المجردة تُضاف إلى كل الفئات الفرعية بنفس القيم لتلك الخصائص.
لحل هذه المشكلة، عندما تستخدم related_name
أو related_query_name
في فئة أساسية مجردة، يجب أن تحتوي القيمة على %(app_label)s
و %(class)s
.
%(class)s
يتم استبداله باسم الفئة الفرعية (بالأحرف الصغيرة).%(app_label)s
يتم استبداله باسم التطبيق الذي يحتوي على الفئة الفرعية (بالأحرف الصغيرة).
مثال على استخدام %(app_label)s
و %(class)s
from django.db import models
class Base(models.Model):
m2m = models.ManyToManyField(
OtherModel,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss",
)
class Meta:
abstract = True
class ChildA(Base):
pass
class ChildB(Base):
pass
في هذا المثال:
-
عند استخدام
%(app_label)s
و%(class)s
فيrelated_name
وrelated_query_name
، ستتمكن من تجنب التعارضات بين الحقول في الفئات الفرعية المختلفة. -
إذا لم تحدد
related_name
في الفئة الأساسية المجردة، فسيتم استخدام اسم الفئة الفرعية متبوعًا بـ_set
كاسم عكسي، مثلchilda_set
وchildb_set
.
وراثة النماذج متعددة الجداول (Multi-table inheritance) في Django
في وراثة النماذج متعددة الجداول، كل نموذج في التسلسل الهرمي يعتبر نموذجًا مستقلًا بحد ذاته، مع جدول قاعدة بيانات خاص به. العلاقة بين النماذج تُنشئ روابط بين النموذج الفرعي وكل من النماذج الأصلية (من خلال OneToOneField
يتم إنشاؤه تلقائيًا). هذا النوع من الوراثة يسمح بالنماذج المستقلة التي يمكن استعلامها وإنشاؤها بشكل فردي.
مثال على وراثة متعددة الجداول
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
في هذا المثال:
Place
هو نموذج أساسي يحتوي على حقولname
وaddress
.Restaurant
هو نموذج فرعي منPlace
ويضيف حقولًا إضافية مثلserves_hot_dogs
وserves_pizza
.
الاستعلامات في وراثة متعددة الجداول
على الرغم من أن Place
و Restaurant
هما جدولان منفصلان في قاعدة البيانات، يمكنك الاستعلام عنهما كما لو كانا في نفس الجدول:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
إذا كان لديك كائن Place
هو أيضًا Restaurant
، يمكنك الوصول إلى الكائن الفرعي باستخدام اسم الفئة الصغيرة (lowercase) للنموذج:
>>> p = Place.objects.get(id=12)
# إذا كان p هو كائن من النوع Restaurant، فسيعطيك الكائن الفرعي:
>>> p.restaurant
<Restaurant: ...>
إذا لم يكن p
من النوع Restaurant
(تم إنشاؤه ككائن Place
فقط أو كان أصلاً نموذجًا آخر)، فإن محاولة الوصول إلى p.restaurant
ستؤدي إلى رفع استثناء Restaurant.DoesNotExist
.
الحقل OneToOneField
الذي يتم إنشاؤه تلقائيًا
في وراثة النماذج متعددة الجداول، يقوم Django بإنشاء حقل OneToOneField
بين Restaurant
و Place
تلقائيًا. هذا الحقل هو الذي ينشئ العلاقة بين الجدولين:
place_ptr = models.OneToOneField(
Place,
on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
يمكنك أيضًا تجاوز هذا الحقل عن طريق تعريف حقل OneToOneField
خاص بك في النموذج الفرعي مع تعيين parent_link=True
.
Meta ووراثة النماذج متعددة الجداول
في حالة وراثة النماذج متعددة الجداول، لا يتم عادةً وراثة فئة Meta
الخاصة بالنموذج الأب. السبب في ذلك هو أن جميع الخيارات الخاصة بـ Meta
قد تم تطبيقها بالفعل في النموذج الأب، وبالتالي فإن إعادة تطبيقها في النموذج الفرعي قد يؤدي إلى سلوك غير مرغوب فيه.
على عكس حالة الفئة الأساسية المجردة (Abstract Base Class)، حيث أن الفئة الأب لا تُنشأ جدول قاعدة بيانات خاص بها، في وراثة النماذج متعددة الجداول يعتبر كل نموذج متميزًا وله جدول خاص به.
عدم وراثة Meta
في النماذج الفرعية
إذا لم يُحدد النموذج الفرعي خصائص مثل ordering
أو get_latest_by
في فئة Meta
الخاصة به، فسيتم وراثتها من النموذج الأب. على سبيل المثال:
class ChildModel(ParentModel):
# ...
class Meta:
# إزالة تأثير ترتيب النموذج الأب
ordering = []
في هذا المثال، إذا كان ParentModel
يحتوي على ترتيب (ordering
) معين، فإن النموذج الفرعي ChildModel
يحدد ترتيبًا فارغًا (ordering = []
) لإلغاء تأثير ترتيب الأب.
ملخص
- وراثة النماذج متعددة الجداول تتيح لكل نموذج في التسلسل الهرمي أن يكون له جدول قاعدة بيانات خاص به.
- يتم إنشاء ارتباط بين النماذج عبر حقل
OneToOneField
الذي يُضاف تلقائيًا. - في حالة وجود فئة
Meta
في النموذج الأب، لا يتم وراثتها بشكل تلقائي في النموذج الفرعي إلا في بعض الحالات (مثلordering
). - يمكن تخصيص
Meta
في النموذج الفرعي لإلغاء تأثيرات الأب أو لتحديد إعدادات جديدة.
الوراثة والعلاقات العكسية في Django
في وراثة النماذج متعددة الجداول، يتم استخدام OneToOneField
بشكل ضمني لربط النموذج الفرعي بالنموذج الأب، مما يسمح بالانتقال من النموذج الأب إلى النموذج الفرعي بسهولة، كما هو موضح في المثال السابق. ومع ذلك، هذا يؤدي إلى استخدام الاسم الذي يكون هو القيمة الافتراضية لـ related_name
في العلاقات من نوع ForeignKey
و ManyToManyField
.
إذا كنت تضيف هذه الأنواع من العلاقات على نموذج فرعي من النموذج الأب، يجب عليك تحديد خاصية related_name
لكل حقل من هذه الحقول. إذا نسيت القيام بذلك، سيرتفع خطأ تحقق (validation error) في Django.
مثال على مشكلة في العلاقات العكسية
باستخدام نموذج Place
كما في المثال السابق، دعنا ننشئ نموذجًا فرعيًا جديدًا مع حقل ManyToManyField
:
class Supplier(Place):
customers = models.ManyToManyField(Place)
سيؤدي هذا إلى الخطأ التالي:
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.
HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
حل المشكلة: تحديد related_name
لحل هذه المشكلة، يجب عليك إضافة خاصية related_name
إلى الحقل ManyToManyField
، بحيث يختلف اسم العلاقة العكسية عن place_ptr
:
class Supplier(Place):
customers = models.ManyToManyField(Place, related_name='provider')
في هذا المثال، قمنا بتحديد related_name='provider'
، مما يمنع حدوث تعارض بين اسم العلاقة العكسية للـ customers
و place_ptr
.
تحديد حقل الرابط الأبوي (Parent Link Field)
كما ذكرنا، يقوم Django بإنشاء OneToOneField
بشكل تلقائي لربط النموذج الفرعي بالنموذج الأب إذا كان الأب غير مجرد (non-abstract). ولكن إذا أردت التحكم في اسم السمة التي تربط النموذج الفرعي بالنموذج الأب، يمكنك إنشاء OneToOneField
خاص بك وتعيين parent_link=True
للإشارة إلى أن الحقل هو الرابط إلى النموذج الأب.
مثال على تحديد حقل الرابط الأبوي
class Supplier(Place):
place_ptr = models.OneToOneField(
Place,
on_delete=models.CASCADE,
parent_link=True
)
# الحقول الأخرى هنا
في هذا المثال، قمنا بتحديد place_ptr
كحقل OneToOneField
مع parent_link=True
، مما يمنحك التحكم الكامل في اسم هذا الرابط الأبوي.
الملخص
- في وراثة النماذج متعددة الجداول، يتم إنشاء
OneToOneField
بشكل ضمني لربط النموذج الفرعي بالنموذج الأب. - يجب تحديد
related_name
في العلاقات من نوعForeignKey
وManyToManyField
في النماذج الفرعية لتجنب تعارضات أسماء العلاقات العكسية. - إذا أردت تخصيص اسم العلاقة الأبوي، يمكنك إنشاء
OneToOneField
مخصص معparent_link=True
.
نموذج الوكيل (Proxy Models)
في وراثة النماذج متعددة الجداول، يتم إنشاء جدول قاعدة بيانات جديد لكل فئة فرعية من النموذج. وهذا سلوك مرغوب فيه عادةً، حيث يحتاج النموذج الفرعي إلى مكان لتخزين البيانات الإضافية التي لا توجد في النموذج الأساسي. ومع ذلك، في بعض الحالات، قد ترغب في تغيير السلوك البرمجي للنموذج فقط – على سبيل المثال، تغيير المدير الافتراضي أو إضافة طريقة جديدة، دون الحاجة إلى تعديل الجدول أو البيانات.
ما هو نموذج الوكيل؟
نموذج الوكيل (Proxy model) يسمح لك بإنشاء "وكيل" للنموذج الأصلي. يمكن إنشاء وتحديث وحذف الحالات (instances) لنموذج الوكيل وكأنك تستخدم النموذج الأصلي (غير الوكيل). الفرق هو أنه يمكنك تعديل سلوكيات مثل ترتيب النموذج الافتراضي أو المدير الافتراضي داخل الوكيل، دون الحاجة إلى تعديل النموذج الأصلي.
كيفية تعريف نموذج الوكيل
يتم تعريف نموذج الوكيل كما هو الحال مع النماذج العادية. الفرق هو أنك تُعلم Django أنه نموذج وكيل من خلال تعيين خاصية proxy=True
في الفئة Meta
.
مثال على نموذج الوكيل
افترض أنك تريد إضافة طريقة جديدة إلى نموذج Person
. يمكنك القيام بذلك كما يلي:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# إضافة سلوك مخصص هنا
pass
في هذا المثال، فئة MyPerson
تعمل على نفس جدول قاعدة البيانات مثل فئة Person
الأصلية. بشكل خاص، ستكون أي حالات جديدة من Person
متاحة أيضًا عبر MyPerson
، والعكس صحيح:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
استخدام نموذج الوكيل لتغيير ترتيب البيانات
يمكنك أيضًا استخدام نموذج الوكيل لتعريف ترتيب افتراضي مختلف على النموذج. على سبيل المثال، قد لا ترغب دائمًا في ترتيب نموذج Person
، ولكنك تريد ترتيب البيانات دائمًا بواسطة حقل last_name
عند استخدام الوكيل:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
الآن، الاستعلامات العادية على Person
ستكون غير مرتبة، بينما ستكون الاستعلامات على OrderedPerson
مرتبة دائمًا حسب last_name
.
الخصائص المتعلقة بـ Meta في نماذج الوكيل
- وراثة الخصائص من Meta: نماذج الوكيل ترث خصائص الـ Meta تمامًا كما هو الحال مع النماذج العادية.
- التغييرات المحدودة: يسمح نموذج الوكيل بتغيير سلوك النموذج (مثل الترتيب أو المدير الافتراضي) دون التأثير على النموذج الأساسي أو قاعدة البيانات نفسها.
الملخص
- نماذج الوكيل هي نماذج لا تُنشئ جدولًا جديدًا في قاعدة البيانات، لكنها تغير فقط سلوكيات نموذج موجود.
- يمكنك تغيير ترتيب البيانات أو إضافة طرق جديدة للنموذج عبر نموذج الوكيل.
- يتم تحديد نموذج الوكيل باستخدام
proxy = True
في فئةMeta
.
الاستعلامات لا تعيد إلا النموذج المطلوب
عند استخدام نماذج الوكيل في Django، إذا قمت بإجراء استعلام على النموذج الأصلي (مثل Person
)، فسيتم إرجاع الكائنات من النوع الأصلي فقط (مثل Person
)، حتى إذا كنت تستخدم نموذج وكيل مثل MyPerson
. فكرة نماذج الوكيل هي أنه يمكن كودك الخاص استخدام التمديدات التي أضفتها (مثل الأساليب أو التعديلات الأخرى)، بينما يبقى الكود الذي يعتمد على النموذج الأصلي يستخدم الكائنات التي تعود منه فقط. نماذج الوكيل لا تعدل سلوك الاستعلامات بحيث تستبدل الكائنات الأصلية بنموذج الوكيل.
قيود النموذج الأساسي (Base Class Restrictions)
- يجب أن يرث نموذج الوكيل من نموذج واحد فقط غير تجريدي (non-abstract) لكي يعمل بشكل صحيح.
- لا يمكنك وراثة أكثر من نموذج غير تجريدي واحد، حيث أن نموذج الوكيل لا يوفر أي ارتباط بين الصفوف في الجداول المختلفة في قاعدة البيانات.
- يمكنك أن ترث من أي عدد من النماذج التجريدية (abstract models)، بشرط ألا تعرف تلك النماذج حقولًا (fields) للنموذج.
- يمكن لنموذج الوكيل أن يرث من أي عدد من نماذج الوكيل التي تشترك في نموذج أساسي غير تجريدي مشترك.
مديرين نماذج الوكيل (Proxy Model Managers)
- المدير الافتراضي: إذا لم تحدد أي مديرين لنموذج الوكيل، فسيتم توريث المديرين من النموذج الأساسي.
- تحديد مدير مخصص: إذا قمت بتحديد مدير مخصص لنموذج الوكيل، سيصبح هو المدير الافتراضي عند إجراء الاستعلامات عليه. ومع ذلك، ستظل المديرين المحددين في النماذج الأساسية متاحة.
مثال على تغيير المدير الافتراضي لنموذج الوكيل
إذا أردت تغيير المدير الافتراضي عند الاستعلام عن نموذج Person
، يمكنك فعل ذلك كما يلي:
from django.db import models
class NewManager(models.Manager):
# إضافة الأساليب الخاصة بك هنا
pass
class MyPerson(Person):
objects = NewManager() # استخدام المدير الجديد
class Meta:
proxy = True
إضافة مدير جديد دون استبدال المدير الافتراضي
إذا كنت تريد إضافة مدير جديد إلى نموذج الوكيل دون استبدال المدير الافتراضي، يمكنك اتباع تقنيات مشابهة لتلك التي تم شرحها في توثيق المدير المخصص، على سبيل المثال:
# إنشاء فئة تجريدية لمدير جديد.
class ExtraManagers(models.Model):
secondary = NewManager() # إضافة مدير جديد
class Meta:
abstract = True
class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
ملاحظة: بينما لا تحتاج عادة إلى هذا النوع من التخصيصات، فإن Django يتيح لك القيام بذلك عندما تحتاج إلى تخصيص إضافي.
الاختلافات بين الوراثة عبر نماذج الوكيل (Proxy) والنماذج غير المدارة (Unmanaged)
في Django، قد تبدو الوراثة عبر نماذج الوكيل مشابهة إلى حد ما لخلق نموذج غير مُدار، باستخدام السمة managed
في فئة الـ Meta. ولكن هناك فروقات أساسية بين هذين النوعين من النماذج:
1. نماذج الوكيل (Proxy Models):
- الهدف: تم تصميم نماذج الوكيل لتغيير السلوك البرمجي للنموذج الأصلي دون التأثير على هيكل قاعدة البيانات. بعبارة أخرى، النموذج الوكيل يعمل على نفس الجدول في قاعدة البيانات للنموذج الأساسي، لكنه يوفر سلوكًا إضافيًا أو مخصصًا (مثل تغيير المديريين أو إضافة أساليب جديدة).
- التوريث: نماذج الوكيل ترث كافة الحقول والمديرين من النموذج الأساسي. يتم حفظ البيانات في نفس الجدول.
- المزامنة: نظرًا لأن نموذج الوكيل يرث الحقول من النموذج الأصلي، فإنه يظل دائمًا متزامنًا مع النموذج الأساسي. أي تغيير في النموذج الأساسي (مثل إضافة أو تعديل حقل) سينعكس مباشرة في نموذج الوكيل.
- الإعداد: لتحديد نموذج وكيل، يتم تعيين السمة
proxy=True
في الـ Meta class.
2. النماذج غير المدارة (Unmanaged Models):
- الهدف: النماذج غير المدارة هي تلك التي لا يديرها Django في قاعدة البيانات. هذا يعني أن Django لا يطبق العمليات المعتادة مثل إنشاء الجدول أو تعديله عند إجراء العمليات على قاعدة البيانات (مثل
migrate
). يتم استخدام هذا النوع من النماذج عندما تحتاج إلى مراعاة جداول قاعدة البيانات الموجودة التي يتم التحكم فيها من خارج Django، مثل جداول عرض (Views) أو جداول تم إنشاؤها يدويًا. - التوريث: لا يرث النموذج غير المدارة من أي نموذج آخر بشكل طبيعي. ولكنه يمكن أن يحدد جدولًا باستخدام
Meta.db_table
ليشير إلى جدول خارجي معين. لا يتم إنشاء أو تعديل هذا الجدول بواسطة Django. - المزامنة: النماذج غير المدارة لا تتم مزامنتها تلقائيًا مع النماذج الأصلية لأن Django لا يدير قاعدة البيانات. إذا كنت تستخدم
Meta.managed=False
معMeta.db_table
، يجب عليك التأكد يدويًا من أن الهيكل و/أو الحقول متوافقة. - الإعداد: لتحديد نموذج غير مدر، يتم تعيين السمة
managed=False
في الـ Meta class.
الفروقات الرئيسية:
الميزة | نموذج الوكيل (Proxy Model) | النموذج غير المدار (Unmanaged Model) |
---|---|---|
الهدف | تعديل السلوك البرمجي (مثل إضافة أساليب أو تغيير المدير) | الوصول إلى الجداول الموجودة أو قواعد البيانات غير المدارة بواسطة Django |
التوريث | يرث الحقول والمديرين من النموذج الأساسي | لا يرث الحقول، ويمكن تحديد جدول محدد عبر Meta.db_table |
المزامنة | مزامنة تامة مع النموذج الأساسي | لا يوجد مزامنة تلقائية مع النموذج الأساسي، والبيانات يجب أن تتم إدارتها يدويًا |
التخزين في قاعدة البيانات | يستخدم نفس الجدول في قاعدة البيانات | لا يدير الجدول، يمكن أن يشير إلى جدول خارجي (مثل جدول عرض) |
السمة في Meta | proxy=True | managed=False |
متى تستخدم كل نوع؟
- استخدم نماذج الوكيل عندما تريد تعديل السلوك البرمجي لنموذج موجود بالفعل (على سبيل المثال، تغيير ترتيب النتائج أو إضافة أساليب جديدة) مع الاحتفاظ بالهيكل والبيانات نفسها في قاعدة البيانات.
- استخدم النماذج غير المدارة عندما تتعامل مع جداول موجودة بالفعل في قاعدة البيانات ولا ترغب في أن يديرها Django (مثل الجداول التي يتم إنشاؤها يدويًا أو الجداول الخاصة بالجداول العرضية أو الجداول التي يتم التحكم فيها من قبل أنظمة أخرى).
وراثة متعددة النماذج في Django
كما هو الحال في Python، يمكن لنموذج Django أن يرث من عدة نماذج أصلية. ومع ذلك، يجب أن تكون حذرًا من بعض القواعد الخاصة بالوراثة متعددة الأصول في Django والتي قد تؤدي إلى مشاكل إذا لم تتم إدارتها بشكل صحيح.
القواعد الأساسية:
-
قواعد الحل للمسمّيات في Python: في حالة الوراثة متعددة النماذج، تتبع Django قواعد الحل للمسمّيات في Python. هذا يعني أنه إذا كان هناك أكثر من نموذج يحتوي على فئة
Meta
أو أي فئة أخرى بنفس الاسم، فإن Django سيستخدم أول فئة تظهر في ترتيب الوراثة وسيغض النظر عن أي فئات أخرى بنفس الاسم في النماذج التالية. -
الوراثة من نماذج متعددة: الوراثة من نماذج متعددة ليست شائعة في معظم الأحيان. الفائدة الرئيسية من الوراثة متعددة النماذج هي استخدام فئات الاندماج (Mixins)، وهي فئات تحتوي على وظائف أو حقول معينة ترغب في إضافتها إلى عدة نماذج.
-
التحديات عند استخدام حقول مفتاح أساسي مشترك: إذا كنت ترث من نماذج متعددة تحتوي كل منها على حقل المفتاح الأساسي، فإن ذلك سيؤدي إلى خطأ في Django. وذلك لأن Django لا يمكنه أن يحدد أي حقل هو المفتاح الأساسي الرئيسي في حالة وجود أكثر من حقل مشابه.
حلول لتجنب مشكلة المفتاح الأساسي المشترك:
1. استخدام حقل AutoField صريح في النماذج الأساسية: إذا كان كل نموذج يرث من حقل مفتاح أساسي مختلف، يمكنك تحديد حقل AutoField
في كل نموذج لتجنب التعارض.
class Article(models.Model):
article_id = models.AutoField(primary_key=True)
# other fields...
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
# other fields...
class BookReview(Book, Article):
pass
2. استخدام سلف مشترك يحتوي على AutoField: إذا كنت تريد استخدام وراثة متعددة النماذج بحيث يحتوي كل نموذج على مفتاح أساسي مشترك، يمكنك إنشاء نموذج سلف مشترك يحتوي على حقل AutoField
، ثم استخدام OneToOneField
من كل نموذج فرعي إلى السلف المشترك.
class Piece(models.Model):
pass
class Article(Piece):
article_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
# other fields...
class Book(Piece):
book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
# other fields...
class BookReview(Book, Article):
pass
ملاحظات إضافية:
- الوراثة متعددة النماذج مفيدة في حالات فئات الاندماج، حيث يتم إضافة حقل أو أسلوب معين إلى جميع النماذج الموروثة.
- تجنب استخدام الوراثة متعددة النماذج عندما لا تكون بحاجة إليها. الوراثة البسيطة عادة ما تكون أفضل للحفاظ على وضوح الهيكل وتجنب التعقيد غير الضروري.
إخفاء أسماء الحقول في Django
في الوراثة العادية في Python، يمكن للطفل (الوريث) تجاوز أي سمة من السمة الخاصة بالفئة الأم. ولكن في Django، لا يُسمح عادة بتجاوز حقول النماذج الموروثة من الفئات الأم.
القيود المتعلقة بإخفاء أسماء الحقول:
-
الحقل الموروث من نموذج غير تجريدي: إذا كان لديك نموذج غير تجريدي يحتوي على حقل باسم
author
، فلا يمكنك إنشاء حقل آخر بنفس الاسم في نموذج موروث من هذا النموذج. -
الحقول الموروثة من نموذج تجريدي: الحقول الموروثة من نموذج تجريبي يمكن تجاوزها أو حتى إزالتها عن طريق تعيين
field_name = None
.
لماذا توجد هذه القيود؟
- التحقيق في الحقول: عندما تحاول تجاوز حقل في نموذج موروث، قد يؤدي ذلك إلى مشاكل عند إنشاء الحقول الجديدة أو عند التسلسل.
- التعريف أثناء الفئة: Django يعتمد على وجود الحقول في فئة النموذج عند تعريفها، مما قد يتسبب في صعوبة التعامل مع بعض الميزات مثل التهيئة أو التسلسل عند تجاوز الحقول.
- الحقول التي تحتوي على سمات إضافية: بعض الحقول مثل
ForeignKey
تحتوي على سمات إضافية مثل_id
وrelated_name
وrelated_query_name
التي يجب أن تُدار بشكل صحيح. لا يمكن تجاوز هذه السمات الإضافية إلا إذا تم تعديل الحقل الذي يعرّفها.
الحل في حالة الوراثة متعددة النماذج:
في الوراثة متعددة النماذج، عندما تُورث الحقول من نماذج تجريبية متعددة، يتم تحديد ترتيب الحل بشكل صارم باستخدام عمق أولاً، مما يختلف عن مبدأ الحل في Python الذي يعتمد على البحث العرضي في حالة الوراثة المتعددة.
تنظيم النماذج في حزمة (Package):
عند تطوير تطبيق يحتوي على العديد من النماذج، قد يكون من المفيد تنظيمها في ملفات منفصلة. بدلاً من وجود ملف models.py
واحد، يمكنك إنشاء حزمة نماذج داخل مجلد التطبيق.
طريقة إنشاء حزمة للنماذج:
-
إنشاء مجلد خاص بالنماذج:
- قم بإزالة ملف
models.py
في التطبيق الخاص بك. - أنشئ مجلدًا باسم
models
داخل التطبيق. - أضف ملف
__init__.py
في هذا المجلد.
- قم بإزالة ملف
-
تنظيم النماذج داخل ملفات منفصلة:
- يمكنك تقسيم النماذج إلى ملفات منفصلة مثل
organic.py
وsynthetic.py
. - في ملف
__init__.py
، استورد النماذج من هذه الملفات:
- يمكنك تقسيم النماذج إلى ملفات منفصلة مثل
# myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot
فوائد هذه الطريقة:
- تحسين وضوح الكود: تنظيم النماذج في ملفات منفصلة يجعل الكود أكثر وضوحًا وقابلية للصيانة.
- تجنب ازدحام المساحة الاسمية: تجنب استخدام
from .models import *
للحفاظ على مساحة أسماء نظيفة. - سهولة تحليل الكود: يساعد هذا التنظيم في جعل أدوات تحليل الكود أكثر فعالية.
ملاحظات إضافية:
- لا تستخدم الوراثة متعددة النماذج المعقدة إلا إذا كان لديك سبب قوي لذلك. حاول الحفاظ على التسلسل الهرمي للنماذج بسيطًا لتجنب التعقيد غير الضروري.