حول المحتوى:
تعرف على كل ما تحتاجه حول Django Models في الإصدار الخامس. من الحقول والعلاقات إلى الوراثة والتخصيصات، يوفر هذا الدليل الشامل شرحًا تفصيليًا لأهم ميزات النماذج وكيفية الاستفادة منها لبناء قواعد بيانات قوية وفعالة في تطبيقاتك.
النموذج هو المصدر الوحيد والموثوق للمعلومات حول بياناتك. يحتوي على الحقول والسلوكيات الأساسية للبيانات التي تقوم بتخزينها. بشكل عام، يتوافق كل نموذج مع جدول قاعدة بيانات واحد.
الأساسيات:
django.db.models.Model
.مثال سريع:
النموذج التالي يعرّف شخصًا (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 مخصص يتناسب مع قاعدة البيانات المحددة في ملف الإعدادات الخاص بك.بمجرد تعريف النماذج الخاصة بك، تحتاج إلى إخبار 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) الخاصة بها.
أهم جزء في النموذج – والجزء الوحيد الذي يجب أن يكون موجودًا – هو قائمة الحقول التي يحددها في قاعدة البيانات. يتم تحديد الحقول كسمات (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
.
كل حقل في النموذج يجب أن يكون مثيلاً لإحدى فئات الحقول المناسبة في Django. تُستخدم أنواع فئات الحقول لتحديد العديد من الأمور:
INTEGER
, VARCHAR
, TEXT
).<input type="text">
, <select>
).يأتي Django مع العديد من أنواع الحقول الجاهزة؛ يمكنك العثور على القائمة الكاملة في مرجع حقول النماذج. إذا لم تكن الحقول المضمنة مناسبة، يمكنك بسهولة كتابة الحقول الخاصة بك؛ راجع كيفية إنشاء حقول نماذج مخصصة.
تقبل كل حقل مجموعة من الوسائط (arguments) الخاصة به والمحددة لنوع الحقل. على سبيل المثال، حقل CharField
(وفئاته الفرعية) يتطلب الوسيطة max_length
التي تحدد حجم العمود من نوع VARCHAR
المستخدم لتخزين البيانات.
هناك أيضًا مجموعة من الوسائط المشتركة المتاحة لجميع أنواع الحقول. جميعها اختيارية، وتُشرح بالتفصيل في المرجع. فيما يلي ملخص سريع لأكثرها استخدامًا:
null
:
True
، يقوم Django بتخزين القيم الفارغة كـ NULL
في قاعدة البيانات. القيمة الافتراضية هي False
.blank
:
إذا كانت True
، يُسمح بترك الحقل فارغًا. القيمة الافتراضية هي False
.
ملاحظة:
null
يتعلق بقاعدة البيانات فقط، بينماblank
يتعلق بالتحقق من صحة النموذج. إذا كان الحقل يحتوي علىblank=True
، فإن التحقق من صحة النموذج يسمح بإدخال قيمة فارغة. أما إذا كانblank=False
، فإن الحقل سيكون مطلوبًا.
choices
:
select box
) بدلاً من حقل نصي عادي، وسيتم تقييد الاختيارات بالقيم المقدمة.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'
يمكنك استخدام فئات التعداد (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)
تعمل فئات التعداد على تقليل التعقيد عند تعريف اختيارات ثابتة، مما يجعل الكود أكثر وضوحًا وسهولة في الصيانة.
راجع مرجع حقول النماذج.
default
callable
). إذا كانت دالة، يتم استدعاؤها في كل مرة يتم فيها إنشاء كائن جديد.from django.db import models
from datetime import datetime
class Event(models.Model):
created_at = models.DateTimeField(default=datetime.now)
db_default
CURRENT_TIMESTAMP
). db_default
و default
، فسيتم استخدام default
عند إنشاء الكائنات عبر ORM.db_default
عند إدراج الصفوف خارج ORM أو عند إضافة حقل جديد في الترحيلات.help_text
class Book(models.Model):
title = models.CharField(max_length=100, help_text="أدخل عنوان الكتاب هنا.")
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']>
unique
True
، يجب أن يكون الحقل فريدًا عبر الجدول.class User(models.Model):
email = models.EmailField(unique=True)
id
) من النوع المحدد في AppConfig.default_auto_field
أو في الإعداد DEFAULT_AUTO_FIELD
.id = models.BigAutoField(primary_key=True)
primary_key=True
على أحد الحقول.primary_key
(سواء تم تعريفه صراحةً أو تمت إضافته تلقائيًا).ForeignKey
, ManyToManyField
, و OneToOneField
) يقبل وسيطًا اختياريًا كأول معطى لتحديد اسم وصفي (verbose name)._
) إلى مسافات.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 طرقًا لتحديد الأنواع الثلاثة الأكثر شيوعًا من العلاقات:
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
) للتحكم في كيفية عمل العلاقة، وهي موضحة في المرجع الخاص بحقول النماذج.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
، وهي موضحة في المرجع الخاص بحقول النماذج.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
) لوصف مجموعة الكائنات المرتبطة.يمكنك تعريف علاقة "عديد إلى عديد" بين كائن ونفسه. على سبيل المثال:
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
: لتحديد جدول وسيط مخصص.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()
add()
يمكنك استخدام add()
لإنشاء علاقة بين الكائنات مع تحديد القيم الافتراضية للحقول الإضافية باستخدام through_defaults
:
from datetime import date
beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
create()
beatles.members.create(
name="George Harrison",
through_defaults={"date_joined": date(1960, 8, 1)}
)
set()
beatles.members.set(
[john, paul, ringo, george],
through_defaults={"date_joined": date(1960, 8, 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)
clear()
# إزالة جميع أعضاء الفرقة
beatles.members.clear()
# حذف جميع سجلات النموذج الوسيط
Membership.objects.all() # QuerySet []
يمكنك استعلام العلاقات "العديد إلى العديد" بالطريقة المعتادة:
# العثور على المجموعات التي تحتوي على عضو يبدأ اسمه بـ "Paul"
Group.objects.filter(members__name__startswith="Paul")
ميزة النموذج الوسيط هي إمكانية استعلام الحقول الإضافية:
# العثور على أعضاء فرقة Beatles الذين انضموا بعد 1 يناير 1961
Person.objects.filter(
group__name="The Beatles",
membership__date_joined__gt=date(1961, 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.
يمكنك استعلام البيانات الوسيطة باستخدام العلاقة العكسية:
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.
لتعريف علاقة "واحد إلى واحد"، نستخدم الحقل OneToOneField
كأي نوع آخر من الحقول في النموذج، مع تضمينه كخاصية داخلية للنموذج.
تُستخدم علاقة "واحد إلى واحد" بشكل شائع عند وجود كائن "يمتد" (extends) من كائن آخر.
على سبيل المثال:
Restaurant
يحتوي على حقل OneToOneField
للإشارة إلى Place
.يتطلب 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
.يمكن أن تشير العلاقة "واحد إلى واحد" إلى نفسها.
يمكنك إنشاء علاقات مع نماذج لم يتم تعريفها بعد باستخدام سلسلة نصية تشير إلى اسم النموذج:
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
يمكنك بسهولة إنشاء علاقة بين نموذج في تطبيق معين ونموذج آخر في تطبيق مختلف. للقيام بذلك:
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,
)
لا يمكن استخدام الكلمات المحجوزة في Python كأسماء للحقول في النماذج، لأنها تسبب أخطاء في البنية (SyntaxError).
class Example(models.Model):
pass = models.IntegerField() # هذا غير مسموح لأن "pass" كلمة محجوزة!
لا يمكن أن يحتوي اسم الحقل على أكثر من شرطة سفلية متتالية (__
) بسبب تعارض مع نظام البحث (lookup) الخاص بـ Django.
class Example(models.Model):
foo__bar = models.IntegerField() # يحتوي على شرطتين سفليتين!
لا يمكن أن ينتهي اسم الحقل بشرطة سفلية (_
) بسبب تعارض مع بنية Django.
يمكنك تجاوز القيود على أسماء الحقول باستخدام الخيار db_column
لتحديد اسم العمود في قاعدة البيانات بشكل مختلف عن اسم الحقل في النموذج.
class Example(models.Model):
my_field = models.IntegerField(db_column='my_custom_field_name')
join
أو select
) كأسماء للحقول.class Example(models.Model):
select = models.CharField(max_length=50) # هذا مسموح لأن Django يعالج الاقتباس.
إذا كانت الحقول المضمنة في Django لا تناسب احتياجاتك، أو إذا كنت ترغب في استخدام نوع بيانات أقل شيوعًا في قاعدة البيانات، يمكنك إنشاء نوع الحقل الخاص بك.
يمكنك استخدام فئة Meta
داخل النموذج لإعطاء معلومات إضافية حول النموذج مثل ترتيب السجلات، أو أسماء الجداول في قاعدة البيانات، أو أسماء البشر المفسرة بصيغة مفردة وجمع.
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"] # تحديد ترتيب السجلات
verbose_name_plural = "oxen" # تحديد الاسم الجمعي للنموذج
ordering
: تحديد ترتيب السجلات عند الاستعلام.verbose_name
: تحديد الاسم المفرد للنموذج.verbose_name_plural
: تحديد الاسم الجمعي للنموذج.db_table
: تحديد اسم الجدول في قاعدة البيانات.unique_together
و index_together
: لتحديد مجموعات فريدة أو فهرسة الحقول معًا.يعد objects
هو المدير الافتراضي للنموذج. يعمل المدير كواجهة لعمليات الاستعلام مع قاعدة البيانات ويتيح لك استرجاع الكائنات من قاعدة البيانات. إذا لم تقم بتعريف مدير مخصص، فإن objects
سيكون هو اسم المدير.
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
# استرجاع جميع الأشخاص
people = Person.objects.all()
يمكنك تعريف طرق مخصصة داخل النموذج لإضافة وظائف على مستوى السجل (الصف). هذه الطرق تسمح لك بتنفيذ منطق العمل (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
لتحويل طريقة إلى خاصية يمكن الوصول إليها مثل الحقل دون الحاجة لاستخدام الأقواس.
يجب أن تعرف بعض الطرق المهمة مثل __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()
لتخصيص سلوك قاعدة البيانات. في بعض الحالات، قد ترغب في تنفيذ إجراءات معينة عندما تقوم بحفظ كائن أو حذفه.
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)
لكي يتم حفظ الكائن في قاعدة البيانات. إذا نسيت استدعاء الطريقة الأصلية، لن يحدث الحفظ ولن يتم تعديل قاعدة البيانات.
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 الأصلية
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 الأصلية
ملاحظة مهمة: عند إجراء عمليات كبيرة مثل الحفظ أو التحديث في كتلة (bulk operations)، فإن الطرق مثل save()
و إشارات pre_save
و post_save
لن تُنفذ. لذا، إذا كنت بحاجة إلى تنفيذ منطق مخصص أثناء العمليات الكبيرة، يجب أن تستخدم إشارات Django مثل pre_save
و post_save
أو إشارات الحذف مثل pre_delete
و post_delete
.
إحدى الأنماط الشائعة هي كتابة استعلامات SQL مخصصة داخل طرق النماذج أو في طرق على مستوى الوحدة (module-level methods). يمكن استخدام SQL الخام (raw SQL) لتنفيذ الاستعلامات مباشرة على قاعدة البيانات في Django، مما يسمح لك بتنفيذ عمليات معقدة لا تدعمها واجهات Django ORM.
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 بشكل مشابه تمامًا للوراثة في Python، لكن من المهم أن تتبع الأساسيات. يجب أن تكون فئة النموذج الأساسية فرعية من django.db.models.Model
.
هناك ثلاثة أنواع من الوراثة التي يمكنك استخدامها في Django:
الفئات الأساسية المجردة (Abstract Base Classes): هذه تستخدم عندما ترغب في إدخال معلومات مشتركة في العديد من النماذج الأخرى. الفئة الأساسية لن تستخدم أبداً لإنشاء جدول قاعدة بيانات، بل سيتم إضافة الحقول الخاصة بها إلى الفئات الفرعية.
الوراثة متعددة الجداول (Multi-table Inheritance): هذه تستخدم عندما تريد لكل نموذج من النماذج الفرعية أن يكون له جدول قاعدة بيانات خاص به.
النماذج الوكيلة (Proxy Models): تستخدم عندما ترغب في تعديل سلوك Python فقط دون تغيير الحقول في النموذج.
تعتبر الفئات الأساسية المجردة مفيدة عندما تريد وضع معلومات مشتركة في عدد من النماذج الأخرى. تكتب الفئة الأساسية مع تعيين 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
أو عن طريق استبدالها بحقل آخر في الفئة الفرعية.عند إنشاء فئة أساسية مجردة (Abstract Base Class)، يقوم Django بتوفير أي فئة Meta
تم تعريفها في الفئة الأساسية كخاصية (attribute). إذا لم تقم الفئة الفرعية بتعريف فئة 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
في الفئة الأساسية المجردة، قد ترغب في تخصيص بعض الخيارات في الفئة الفرعية:
abstract=False
: Django يقوم بتعديل فئة Meta
الخاصة بالفئة الأساسية المجردة عن طريق تعيين abstract=False
، مما يعني أن الفئات الفرعية لا تصبح فئات مجردة تلقائيًا. إذا كنت ترغب في جعل فئة فرعية مجردة أيضًا، يجب عليك تعيين abstract=True
في Meta
الخاصة بها.db_table
: لا ينبغي تضمين db_table
في فئة 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
.
في وراثة النماذج متعددة الجداول، كل نموذج في التسلسل الهرمي يعتبر نموذجًا مستقلًا بحد ذاته، مع جدول قاعدة بيانات خاص به. العلاقة بين النماذج تُنشئ روابط بين النموذج الفرعي وكل من النماذج الأصلية (من خلال 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
قد تم تطبيقها بالفعل في النموذج الأب، وبالتالي فإن إعادة تطبيقها في النموذج الفرعي قد يؤدي إلى سلوك غير مرغوب فيه.
على عكس حالة الفئة الأساسية المجردة (Abstract Base Class)، حيث أن الفئة الأب لا تُنشأ جدول قاعدة بيانات خاص بها، في وراثة النماذج متعددة الجداول يعتبر كل نموذج متميزًا وله جدول خاص به.
Meta
في النماذج الفرعيةإذا لم يُحدد النموذج الفرعي خصائص مثل ordering
أو get_latest_by
في فئة Meta
الخاصة به، فسيتم وراثتها من النموذج الأب. على سبيل المثال:
class ChildModel(ParentModel):
# ...
class Meta:
# إزالة تأثير ترتيب النموذج الأب
ordering = []
في هذا المثال، إذا كان ParentModel
يحتوي على ترتيب (ordering
) معين، فإن النموذج الفرعي ChildModel
يحدد ترتيبًا فارغًا (ordering = []
) لإلغاء تأثير ترتيب الأب.
OneToOneField
الذي يُضاف تلقائيًا.Meta
في النموذج الأب، لا يتم وراثتها بشكل تلقائي في النموذج الفرعي إلا في بعض الحالات (مثل ordering
).Meta
في النموذج الفرعي لإلغاء تأثيرات الأب أو لتحديد إعدادات جديدة.في وراثة النماذج متعددة الجداول، يتم استخدام 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
.
كما ذكرنا، يقوم 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 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
.
proxy = True
في فئة Meta
.عند استخدام نماذج الوكيل في Django، إذا قمت بإجراء استعلام على النموذج الأصلي (مثل Person
)، فسيتم إرجاع الكائنات من النوع الأصلي فقط (مثل Person
)، حتى إذا كنت تستخدم نموذج وكيل مثل MyPerson
. فكرة نماذج الوكيل هي أنه يمكن كودك الخاص استخدام التمديدات التي أضفتها (مثل الأساليب أو التعديلات الأخرى)، بينما يبقى الكود الذي يعتمد على النموذج الأصلي يستخدم الكائنات التي تعود منه فقط. نماذج الوكيل لا تعدل سلوك الاستعلامات بحيث تستبدل الكائنات الأصلية بنموذج الوكيل.
إذا أردت تغيير المدير الافتراضي عند الاستعلام عن نموذج 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 يتيح لك القيام بذلك عندما تحتاج إلى تخصيص إضافي.
في Django، قد تبدو الوراثة عبر نماذج الوكيل مشابهة إلى حد ما لخلق نموذج غير مُدار، باستخدام السمة managed
في فئة الـ Meta. ولكن هناك فروقات أساسية بين هذين النوعين من النماذج:
proxy=True
في الـ Meta class.migrate
). يتم استخدام هذا النوع من النماذج عندما تحتاج إلى مراعاة جداول قاعدة البيانات الموجودة التي يتم التحكم فيها من خارج Django، مثل جداول عرض (Views) أو جداول تم إنشاؤها يدويًا.Meta.db_table
ليشير إلى جدول خارجي معين. لا يتم إنشاء أو تعديل هذا الجدول بواسطة Django.Meta.managed=False
مع Meta.db_table
، يجب عليك التأكد يدويًا من أن الهيكل و/أو الحقول متوافقة.managed=False
في الـ Meta class.الميزة | نموذج الوكيل (Proxy Model) | النموذج غير المدار (Unmanaged Model) |
---|---|---|
الهدف | تعديل السلوك البرمجي (مثل إضافة أساليب أو تغيير المدير) | الوصول إلى الجداول الموجودة أو قواعد البيانات غير المدارة بواسطة Django |
التوريث | يرث الحقول والمديرين من النموذج الأساسي | لا يرث الحقول، ويمكن تحديد جدول محدد عبر Meta.db_table |
المزامنة | مزامنة تامة مع النموذج الأساسي | لا يوجد مزامنة تلقائية مع النموذج الأساسي، والبيانات يجب أن تتم إدارتها يدويًا |
التخزين في قاعدة البيانات | يستخدم نفس الجدول في قاعدة البيانات | لا يدير الجدول، يمكن أن يشير إلى جدول خارجي (مثل جدول عرض) |
السمة في Meta | proxy=True | managed=False |
كما هو الحال في 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
في الوراثة العادية في Python، يمكن للطفل (الوريث) تجاوز أي سمة من السمة الخاصة بالفئة الأم. ولكن في Django، لا يُسمح عادة بتجاوز حقول النماذج الموروثة من الفئات الأم.
الحقل الموروث من نموذج غير تجريدي: إذا كان لديك نموذج غير تجريدي يحتوي على حقل باسم author
، فلا يمكنك إنشاء حقل آخر بنفس الاسم في نموذج موروث من هذا النموذج.
الحقول الموروثة من نموذج تجريدي: الحقول الموروثة من نموذج تجريبي يمكن تجاوزها أو حتى إزالتها عن طريق تعيين field_name = None
.
ForeignKey
تحتوي على سمات إضافية مثل _id
و related_name
و related_query_name
التي يجب أن تُدار بشكل صحيح. لا يمكن تجاوز هذه السمات الإضافية إلا إذا تم تعديل الحقل الذي يعرّفها.في الوراثة متعددة النماذج، عندما تُورث الحقول من نماذج تجريبية متعددة، يتم تحديد ترتيب الحل بشكل صارم باستخدام عمق أولاً، مما يختلف عن مبدأ الحل في Python الذي يعتمد على البحث العرضي في حالة الوراثة المتعددة.
عند تطوير تطبيق يحتوي على العديد من النماذج، قد يكون من المفيد تنظيمها في ملفات منفصلة. بدلاً من وجود ملف 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 *
للحفاظ على مساحة أسماء نظيفة.
تعرف على كل ما تحتاجه حول Django Models في الإصدار الخامس. من الحقول والعلاقات إلى الوراثة والتخصيصات، يوفر هذا الدليل الشامل شرحًا تفصيليًا لأهم ميزات النماذج وكيفية الاستفادة منها لبناء قواعد بيانات قوية وفعالة في تطبيقاتك.
مساحة اعلانية
المكتبة في لغة البرمجة هي مجموعة من الأوامر البرمجية المجهزة مسبقا لتنفيذ مهام محددة، تمكنك هذه المكاتب من التعامل معها ضمن مشروعك.