دليلك الشامل إلى Django 5 Models

النماذج (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) بدلاً من حقل نصي عادي، وسيتم تقييد الاختيارات بالقيم المقدمة.

مثال على قائمة اختيارات:

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 تلقائيًا بإنشائه باستخدام اسم السمة الخاصة بالحقل، مع تحويل الشرطات السفلية (_) إلى مسافات.

أمثلة على الأسماء الوصفية:

  1. اسم وصفي مخصص:
first_name = models.CharField("person's first name", max_length=30)
  1. اسم وصفي افتراضي:
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 طرقًا لتحديد الأنواع الثلاثة الأكثر شيوعًا من العلاقات:

  1. العلاقات "العديد إلى واحد" (Many-to-One).
  2. العلاقات "العديد إلى العديد" (Many-to-Many).
  3. العلاقات "واحد إلى واحد" (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)

تفاصيل إضافية:

  1. علاقات تكرارية (Recursive Relationships):

    • يمكن أن تكون العلاقة بين كائن ونفسه:
      class Employee(models.Model):
          manager = models.ForeignKey(
              "self",
              on_delete=models.SET_NULL,
              null=True,
              blank=True,
          )
      
  2. علاقات مع نماذج غير معرّفة بعد:

    • يمكن الإشارة إلى نموذج غير مُعرف بعد باستخدام سلسلة نصية:
      class Car(models.Model):
          manufacturer = models.ForeignKey("Manufacturer", on_delete=models.CASCADE)
      
  3. اختيار اسم الحقل:

    • يوصى، ولكن ليس إلزاميًا، بتسمية الحقل باسم النموذج المرتبط بحروف صغيرة (manufacturer في المثال أعلاه). ومع ذلك، يمكن تسميته بأي شيء:
      class Car(models.Model):
          company_that_makes_it = models.ForeignKey(
              Manufacturer,
              on_delete=models.CASCADE,
          )
      

ملاحظات إضافية:

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) تلقائيًا لتخزين العلاقات بين الجداول.

خيارات إضافية:

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)

أفضل الممارسات:

  1. تحديد مكان العلاقة:

    • لا ينبغي أن تضيف ManyToManyField في كلا النموذجين المرتبطين. يكفي أن تكون العلاقة موجودة في أحدهما فقط.
    • في المثال أعلاه، يُفضل وضع العلاقة في نموذج Pizza، حيث من الطبيعي أن يكون المستخدم قادرًا على إضافة تغطيات إلى البيتزا عند تحرير نموذجها.
  2. تخصيص العلاقة:

    • يوفر 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 باستخدام الوسيط 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)

تفسير الكود:

  1. النموذج الأساسي:

    • Person يمثل الأشخاص.
    • Group يمثل المجموعات.
  2. العلاقة "العديد إلى العديد":

    • تم إنشاء العلاقة بين الأشخاص والمجموعات باستخدام الحقل members في نموذج Group.
    • العلاقة تشير إلى النموذج الوسيط Membership من خلال الوسيط through="Membership".
  3. النموذج الوسيط (Membership):

    • يحتوي على الحقول الإضافية date_joined و invite_reason.
    • يربط بين الأشخاص والمجموعات باستخدام الحقول:
      • person (يشير إلى النموذج Person).
      • group (يشير إلى النموذج Group).

قيود النموذج الوسيط:

  1. يجب أن يحتوي النموذج الوسيط على مفتاحين أجنبيين فقط:

    • مفتاح أجنبي إلى النموذج المصدر (مثل Group).
    • مفتاح أجنبي إلى النموذج الهدف (مثل Person).
  2. إذا كان هناك أكثر من مفتاح أجنبي، يجب تحديد الحقول باستخدام through_fields لتجنب أخطاء التحقق.

  3. العلاقات الذاتية: يمكن أن يحتوي النموذج الوسيط على مفتاحين أجنبيين يشيران إلى نفس النموذج (علاقة ذاتية)، ولكن يجب تحديد الحقول بوضوح باستخدام 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)

ملاحظات هامة

  1. الحقل الأساسي (Primary Key):

    • لم يعد حقل OneToOneField يتم تعيينه تلقائيًا كحقل أساسي للنموذج.
    • يمكنك جعله حقلًا أساسيًا يدويًا باستخدام primary_key=True إذا كنت بحاجة لذلك.
    • من الممكن الآن أن يحتوي نموذج واحد على عدة حقول OneToOneField.
  2. المعلمة 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:

  1. الفئات الأساسية المجردة (Abstract Base Classes): هذه تستخدم عندما ترغب في إدخال معلومات مشتركة في العديد من النماذج الأخرى. الفئة الأساسية لن تستخدم أبداً لإنشاء جدول قاعدة بيانات، بل سيتم إضافة الحقول الخاصة بها إلى الفئات الفرعية.

  2. الوراثة متعددة الجداول (Multi-table Inheritance): هذه تستخدم عندما تريد لكل نموذج من النماذج الفرعية أن يكون له جدول قاعدة بيانات خاص به.

  3. النماذج الوكيلة (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 والتي قد تؤدي إلى مشاكل إذا لم تتم إدارتها بشكل صحيح.

القواعد الأساسية:

  1. قواعد الحل للمسمّيات في Python: في حالة الوراثة متعددة النماذج، تتبع Django قواعد الحل للمسمّيات في Python. هذا يعني أنه إذا كان هناك أكثر من نموذج يحتوي على فئة Meta أو أي فئة أخرى بنفس الاسم، فإن Django سيستخدم أول فئة تظهر في ترتيب الوراثة وسيغض النظر عن أي فئات أخرى بنفس الاسم في النماذج التالية.

  2. الوراثة من نماذج متعددة: الوراثة من نماذج متعددة ليست شائعة في معظم الأحيان. الفائدة الرئيسية من الوراثة متعددة النماذج هي استخدام فئات الاندماج (Mixins)، وهي فئات تحتوي على وظائف أو حقول معينة ترغب في إضافتها إلى عدة نماذج.

  3. التحديات عند استخدام حقول مفتاح أساسي مشترك: إذا كنت ترث من نماذج متعددة تحتوي كل منها على حقل المفتاح الأساسي، فإن ذلك سيؤدي إلى خطأ في 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، لا يُسمح عادة بتجاوز حقول النماذج الموروثة من الفئات الأم.

القيود المتعلقة بإخفاء أسماء الحقول:

  1. الحقل الموروث من نموذج غير تجريدي: إذا كان لديك نموذج غير تجريدي يحتوي على حقل باسم author، فلا يمكنك إنشاء حقل آخر بنفس الاسم في نموذج موروث من هذا النموذج.

  2. الحقول الموروثة من نموذج تجريدي: الحقول الموروثة من نموذج تجريبي يمكن تجاوزها أو حتى إزالتها عن طريق تعيين field_name = None.

لماذا توجد هذه القيود؟

  • التحقيق في الحقول: عندما تحاول تجاوز حقل في نموذج موروث، قد يؤدي ذلك إلى مشاكل عند إنشاء الحقول الجديدة أو عند التسلسل.
  • التعريف أثناء الفئة: Django يعتمد على وجود الحقول في فئة النموذج عند تعريفها، مما قد يتسبب في صعوبة التعامل مع بعض الميزات مثل التهيئة أو التسلسل عند تجاوز الحقول.
  • الحقول التي تحتوي على سمات إضافية: بعض الحقول مثل ForeignKey تحتوي على سمات إضافية مثل _id و related_name و related_query_name التي يجب أن تُدار بشكل صحيح. لا يمكن تجاوز هذه السمات الإضافية إلا إذا تم تعديل الحقل الذي يعرّفها.

الحل في حالة الوراثة متعددة النماذج:

في الوراثة متعددة النماذج، عندما تُورث الحقول من نماذج تجريبية متعددة، يتم تحديد ترتيب الحل بشكل صارم باستخدام عمق أولاً، مما يختلف عن مبدأ الحل في Python الذي يعتمد على البحث العرضي في حالة الوراثة المتعددة.

تنظيم النماذج في حزمة (Package):

عند تطوير تطبيق يحتوي على العديد من النماذج، قد يكون من المفيد تنظيمها في ملفات منفصلة. بدلاً من وجود ملف models.py واحد، يمكنك إنشاء حزمة نماذج داخل مجلد التطبيق.

طريقة إنشاء حزمة للنماذج:

  1. إنشاء مجلد خاص بالنماذج:

    • قم بإزالة ملف models.py في التطبيق الخاص بك.
    • أنشئ مجلدًا باسم models داخل التطبيق.
    • أضف ملف __init__.py في هذا المجلد.
  2. تنظيم النماذج داخل ملفات منفصلة:

    • يمكنك تقسيم النماذج إلى ملفات منفصلة مثل organic.py و synthetic.py.
    • في ملف __init__.py، استورد النماذج من هذه الملفات:
# myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot

فوائد هذه الطريقة:

  • تحسين وضوح الكود: تنظيم النماذج في ملفات منفصلة يجعل الكود أكثر وضوحًا وقابلية للصيانة.
  • تجنب ازدحام المساحة الاسمية: تجنب استخدام from .models import * للحفاظ على مساحة أسماء نظيفة.
  • سهولة تحليل الكود: يساعد هذا التنظيم في جعل أدوات تحليل الكود أكثر فعالية.

ملاحظات إضافية:

  • لا تستخدم الوراثة متعددة النماذج المعقدة إلا إذا كان لديك سبب قوي لذلك. حاول الحفاظ على التسلسل الهرمي للنماذج بسيطًا لتجنب التعقيد غير الضروري.

 

حول المحتوى:

تعرف على كل ما تحتاجه حول Django Models في الإصدار الخامس. من الحقول والعلاقات إلى الوراثة والتخصيصات، يوفر هذا الدليل الشامل شرحًا تفصيليًا لأهم ميزات النماذج وكيفية الاستفادة منها لبناء قواعد بيانات قوية وفعالة في تطبيقاتك.