يوضح هذا المقال كيفية تعريف النماذج Models لموقع المكتبة المحلية LocalLibrary، ويشرح ما هو النموذج وكيفية التصريح عنه وبعض أنواع الحقول الرئيسية، ويعرض بإيجاز بعض الطرق الرئيسية التي يمكنك من خلالها الوصول إلى بيانات النموذج.
- المتطلبات الأساسية: الاطلاع على مقال إنشاء موقع ويب هيكلي لمكتبة محلية.
- الهدف: أن تكون قادرًا على تصميم وإنشاء نماذجك واختيار الحقول بصورة مناسبة.
يمكن لتطبيقات جانغو Django الوصول إلى البيانات وإدارتها من خلال كائنات لغة بايثون Python المُشار إليها بالنماذج Models، إذ تعرِّف النماذج بنية البيانات المخزنة بما في ذلك أنواع الحقول وربما حجمها الأقصى وقيمها الافتراضية وخيارات قائمة الاختيار ونص التعليمات للتوثيق ونص التسمية للاستمارات Forms وغير ذلك. يُعَد تعريف النموذج مستقلًا عن قاعدة البيانات الأساسية، إذ يمكنك اختيار قاعدة بيانات واحدة من عدة قواعد بيانات بوصفها جزءًا من إعدادات مشروعك. لن تحتاج أبدًا إلى التواصل مع قاعدة البيانات التي تريد استخدامها مباشرةً بمجرد اختيارها، فما عليك سوى كتابة بنية نموذجك وشيفرة برمجية أخرى، وسيتولى إطار عمل جانغو كل الأعمال الأخرى للتواصل مع قاعدة البيانات نيابةً عنك.
يوضح هذا المقال كيفية تعريف النماذج والوصول إليها في مثال موقع المكتبة المحلية LocalLibrary.
تتألف هذه السلسلة الفرعية من السلسلة الأشمل تعلم تطوير الويب من المقالات التالية:
- مدخل إلى إطار عمل الويب جانغو Django
- إعداد بيئة تطوير تطبيقات جانغو
- تطبيق عملي لتعلم جانغو - الجزء الأول: إنشاء موقع ويب هيكلي لمكتبة محلية
- تطبيق عملي لتعلم جانغو - الجزء الثاني: استخدام النماذج Models
- تطبيق عملي لتعلم جانغو - الجزء الثالث: موقع مدير جانغو Django Admin
- تطبيق عملي لتعلم جانغو - الجزء الرابع: إنشاء صفحة المكتبة الرئيسية
- تطبيق عملي لتعلم جانغو - الجزء الخامس: العروض Views العامة والتفصيلية
- تطبيق عملي لتعلم جانغو - الجزء السادس: إدارة الجلسات Sessions
- تطبيق عملي لتعلم جانغو - الجزء السابع: استيثاق المستخدمين وأذوناتهم
- تطبيق عملي لتعلم جانغو - الجزء الثامن: العمل مع الاستمارات Forms
- تطبيق عملي لتعلم جانغو - الجزء التاسع: اختبار تطبيق جانغو
- تطبيق عملي لتعلم جانغو - الجزء 10: نشر تطبيق جانغو في بيئة الإنتاج
- تعرف على أمان تطبيقات جانغو
تصميم نماذج المكتبة المحلية LocalLibrary
يُفضَّل أخذ بضع دقائق للتفكير في البيانات التي يجب تخزينها والعلاقات بين الكائنات قبل الانتقال والبدء في كتابة شيفرة النماذج البرمجية. نعلم أننا نحتاج إلى تخزين معلومات حول الكتب (العنوان والملخص والمؤلف واللغة المكتوبة والفئة ورقم ISBN)، وأنه يمكن أن يكون لدينا نسخ متعددة متاحة (مع معرّف فريد عام وحالة التوفر وغير ذلك). يمكن أن نحتاج إلى تخزين مزيد من المعلومات حول المؤلف أكثر من مجرد اسمه، ويمكن أن يكون هناك عدة مؤلفين لهم الاسم نفسه أو أسماء متشابهة. نريد أن نكون قادرين على فرز المعلومات بناءً على عنوان الكتاب والمؤلف واللغة المكتوبة والفئة، إذ يُفضَّل أن يكون لديك نماذج منفصلة لكل كائن (مجموعة من المعلومات المتعلقة ببعضها) عند تصميم نماذجك، والأشياء الواضحة في هذه الحالة هي الكتب ونسخ الكتب والمؤلفون.
قد ترغب في استخدام النماذج لتمثيل خيارات قائمة الاختيار، مثل قائمة اختيارات منسدلة بدلًا من الشيفرة الثابتة للخيارات في موقع الويب نفسه، إذ يُوصَى بذلك عندما لا تكون جميع الخيارات معروفة مسبقًا أو أنها تتغير باستمرار. يشمل المرشحون الواضحون للنماذج في هذه الحالة نوع الكتاب، مثل الخيال العلمي والشعر واللغة العربية والإنجليزية والفرنسية وغير ذلك.
يجب التفكير في العلاقات بمجرد أن نقرر ما هي النماذج والحقول، إذ يسمح جانغو بتعريف العلاقات التي تكون من نوع واحد لواحد "OneToOneField" وواحد إلى متعدد "ForeignKey" ومتعدد إلى متعدد "ManyToManyField".
يوضح مخطط الارتباط التالي باستخدام لغة UML النماذج التي سنعرّفها في هذه الحالة (على شكل مربعات):
أنشأنا نماذجًا للكتاب (التفاصيل العامة للكتاب)، ونسخة الكتاب (حالة النسخ الحقيقية المُحدَّدة للكتاب المتاح في النظام)، والمؤلف، وقررنا أن يكون لدينا نموذج للنوع، بحيث يمكن إنشاء أو تحديد القيم من خلال واجهة المدير، وقررنا عدم وجود نموذج لحالة نسخة الكتاب BookInstance:status
، إذ كتبنا شيفرة ثابتة للقيم (LOAN_STATUS
) لأننا لا نتوقع تغييرها. يمكنك رؤية اسم النموذج وأسماء الحقول وأنواعها والتوابع وأنواع قيمها المُعادة ضمن كل مربع من المربعات.
يوضح المخطط العلاقات بين النماذج بما في ذلك درجة تعدّدها Multiplicities، وهي الأعداد الموجودة على المخطط والتي توضح أعداد (الحد الأقصى والحد الأدنى) كل نموذج والتي يمكن أن تكون موجودةً في العلاقة، فمثلًا يوضّح الخط المتصل بين المربعات أن الكتاب Book والنوع Genre مرتبطان، وتوضح الأعداد القريبة من نموذج النوع أن الكتاب يجب أن يحتوي على نوع واحد أو أكثر (بقدر ما تريد)، بينما توضح الأعداد الموجودة على الطرف الآخر من الخط بجوار نموذج الكتاب أن النوع يمكن ألّا يكون له أيّ نوع مرتبط بالكتاب أو يمكن أن يكون له العديد من الأنواع المرتبطة بالكتب.
ملاحظة: يوفر القسم التالي تمهيدًا يشرح كيفية تعريف النماذج واستخدامها، وضع في بالك أثناء قراءته كيفية بناء كل نموذج من النماذج الموضحة في المخطط السابق.
مدخل إلى النماذج
يقدم هذا القسم نظرةً عامة موجزة عن كيفية تعريف النموذج وبعضًا من أهم الحقول والوسطاء.
تعريف النموذج
تُعرَّف النماذج عادةً في الملف models.py الخاص بالتطبيق، وتُقدَّم بوصفها صنفًا فرعيًا من django.db.models.Model
، ويمكن أن تشمل الحقول والتوابع والبيانات الوصفية. يُظهِر جزء الشيفرة البرمجية التالي نموذجًا قياسيًا بالاسم MyModelName
:
from django.db import models from django.urls import reverse class MyModelName(models.Model): """A typical class defining a model, derived from the Model class.""" # حقول my_field_name = models.CharField(max_length=20, help_text='Enter field documentation') # … # بيانات وصفية class Meta: ordering = ['-my_field_name'] # توابع def get_absolute_url(self): """Returns the URL to access a particular instance of MyModelName.""" return reverse('model-detail-view', args=[str(self.id)]) def __str__(self): """String for representing the MyModelName object (in Admin site etc.).""" return self.my_field_name
سنستكشف في الأقسام التالية كل ميزة في النموذج بالتفصيل.
الحقول Fileds
يمكن أن يحتوي النموذج على عدد عشوائي من الحقول ومن أيّ نوع، إذ يمثل كل حقل عمودًا من البيانات التي نريد تخزينها في أحد جداول قاعدة بياناتنا، ويتألف كل سجل أو صف في قاعدة البيانات من قيمة واحدة لكل قيمة حقل. لنلقِ نظرةً على المثال التالي:
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
يحتوي هذا المثال على حقل واحد يسمى my_field_name
من النوع models.CharField
، مما يعني أن هذا الحقل سيحتوي على سلاسل نصية من المحارف الأبْجَعَددية alphanumeric. تُسنَد أنواع الحقول باستخدام أصناف Classes معينة تحدد نوع السجل المُستخدَم لتخزين البيانات في قاعدة البيانات مع معايير التحقق لاستخدامها عند استلام القيم من استمارة HTML (أي ما يشكّل قيمة صالحة). يمكن أن تأخذ أنواع الحقول وسطاء تحدّد كيفية تخزين الحقل أو كيفية استخدامه، إذ سنعطي الحقل في حالتنا وسيطين، هما:
-
max_length=20
، الذي يشير إلى أن أقصى طول لقيمةٍ ما في هذا الحقل وهو 20 محرفًا. -
help_text='Enter field documentation'
، وهو نص مفيد يمكن عرضه في استمارة لمساعدة المستخدمين على فهم كيفية استخدام الحقل.
يُستخدَم اسم الحقل للإشارة إليه في الاستعلامات والقوالب، وتحتوي الحقول على تسمية Label يحدّدها الوسيط verbose_name
(بقيمة افتراضية هي None
). إذا لم يُضبَط الوسيط verbose_name
، فستُنشَأ التسمية من اسم الحقل من خلال استبدال أيّ شرطات سفلية بمسافة وجعل الحرف الأول حرفًا كبيرًا، فمثلًا سيكون للحقل my_field_name
تسمية افتراضية هي "My field name" عند استخدامه في الاستمارات.
يؤثر ترتيب التصريح عن الحقول على ترتيبها الافتراضي إذا عُرِض نموذج ضمن استمارة (في موقع المدير مثلًا) بالرغم من أن هذا الترتيب يمكن تجاهله.
يمكن استخدام الوسطاء الشائعة التالية عند التصريح عن أنواع الحقول:
-
help_text: يوفر هذا الوسيط تسميةً نصيةً لاستمارات HTML (في موقع المدير مثلًا) كما وضّحنا سابقًا.
-
verbose_name: اسم يمكن أن يقرأه الإنسان للحقل المُستخدَم في تسميات الحقل، وإذا لم يُحدَّد، فسيستنتج جانغو الاسم المُطوَّل Verbose Name الافتراضي من اسم الحقل.
-
default: القيمة الافتراضية للحقل، ويمكن أن يكون هذا الوسيط قيمةً أو كائنًا يمكن استدعاؤه، إذ سيُستدعَى الكائن في هذه الحالة في كل مرة يُنشَأ فيها سجل جديد.
-
null: إذا كانت قيمة هذا الوسيط
True
، فسيخزّن جانغو القيم الفارغة على أنهاNULL
في قاعدة البيانات للحقول التي يكون ذلك مناسبًا لها، وسيخزّن الحقل من النوعCharField
سلسلة نصية فارغة بدلًا من ذلك. القيمة الافتراضية لهذا الوسيط هيFalse
. -
blank: إذا كانت قيمة هذا الوسيط
True
، فسيُسمَح للحقل بأن يكون فارغًا في استماراتك. القيمة الافتراضية لهذا الوسيط هيFalse
، أي سيجبرك التحقق من صحة استمارة جانغو على إدخال قيمة. يُستخدَم هذا الحقل عادةً مع القيمةnull=True
، لأنك إذا أردتَ السماح بقيم فارغة، فأنت تحتاج أيضًا أن تكون قاعدة البيانات قادرة على تمثيلها بصورة مناسبة. -
choices: مجموعة من الاختيارات للحقل، بحيث إذا توفّرت هذه الاختيارات، فستكون أداة الاستمارة المقابلة الافتراضية هي مربع تحديد لهذه الاختيارات بدلًا من حقل النص المعياري.
-
primary_key: إذا كانت قيمة هذا الوسيط
True
، فسيُضبط الحقل الحالي بوصفه مفتاحًا رئيسيًا للنموذج؛ والمفتاح الرئيسي هو عمود خاص في قاعدة البيانات مخصَّص لتعريف جميع سجلات الجدول المختلفة بطريقة فريدة. إذا لم يُحدَّد أيّ حقل بوصفه مفتاحًا رئيسيًا، فسيضيف جانغو تلقائيًا حقلًا لهذا الغرض. يمكن تحديد نوع حقول المفاتيح الرئيسية المُنشَأة تلقائيًا لكل تطبيق فيAppConfig.default_auto_field
أو بصورة عامة في إعدادDEFAULT_AUTO_FIELD
.
ملاحظة: تضبط التطبيقات المُنشَأة باستخدام manage.py نوعَ المفتاح الرئيسي على النوع BigAutoField. يمكنك رؤية ما يلي في الملف catalog/apps.py الخاص بموقع المكتبة المحلية:
class CatalogConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField'
هناك العديد من الخيارات الأخرى، إذ يمكنك الاطلاع القائمة الكاملة لخيارات الحقول في توثيق جانغو.
توضح القائمة التالية بعض أنواع الحقول الأكثر استخدامًا:
-
يُستخدَم النوع CharField لتعريف سلاسل نصية ذات طول ثابت قصير إلى متوسط الحجم. يجب عليك تحديد الطول الأقصى
max_length
للبيانات المراد تخزينها. -
يُستخدَم النوع TextField للسلاسل النصية الكبيرة ذات الطول العشوائي. يمكنك تحديد الطول الأقصى
max_length
للحقل، ولكنه لا يُستخدَم إلا عند عرض الحقل في استمارات (ليس إجباريًا على مستوى قاعدة البيانات). -
النوع IntegerField هو حقل لتخزين القيم الصحيحة (عدد صحيح)، وللتحقق من صحة القيم المدخَلة بوصفها أعدادًا صحيحة في الاستمارات.
-
يُستخدَم النوعان DateField و DateTimeField لتخزين أو تمثيل التواريخ ومعلومات التاريخ/الوقت، مثل كائنات بايثون
datetime.date
وdatetime.datetime
على التوالي. يمكن أن تصرِّح هذه الحقول إضافةً لما سبق عن المعاملات (الحصرية فيما بينها)auto_now=True
(لضبط الحقل على التاريخ الحالي في كل مرة يُحفَظ فيها النموذج) وauto_now_add
(لضبط التاريخ عند إنشاء النموذج لأول مرة فقط) وdefault
(لضبط تاريخ افتراضي يمكن للمستخدم تجاهله). -
يُستخدَم النوع EmailField لتخزين عناوين البريد الإلكتروني والتحقق من صحتها.
-
يُستخدَم النوعان FileField و ImageField لتحميل الملفات والصور على التوالي، إذ يضيف الحقل
ImageField
تحققًا إضافيًا من أن الملف المُحمَّل هو صورة. يمتلك هذان النوعان من الحقول معاملات لتحديد كيفية ومكان تخزين الملفات التي جرى تحميلها. -
النوع AutoField هو نوع خاص من الحقل
IntegerField
الذي يزداد تلقائيًا. يُضاف مفتاح رئيسي من هذا النوع تلقائيًا إلى نموذجك إذا لم تحدده صراحةً. -
يُستخدَم النوع ForeignKey لتحديد علاقة واحد إلى متعدد بنموذج قاعدة بيانات آخر، فمثلًا للسيارة مُصنِّع واحد، ولكن يمكن للمصنِّع صنع العديد من السيارات. الجانب "واحد" من العلاقة هو النموذج الذي يحتوي على المفتاح، وتشير النماذج التي تحتوي على مفتاح خارجي Foreign Key إلى ذلك المفتاح، إذ تكون هذه النماذج في الجانب "متعدد" من هذه العلاقة.
-
يُستخدَم النوع ManyToManyField لتحديد علاقة متعدد إلى متعدد، فمثلًا يمكن أن يكون للكتاب عدة أنواع ويمكن أن يحتوي كل نوع على عدة كتب. سنستخدم هذا النوع في تطبيق المكتبة بطريقة مشابهة جدًا للنوع
ForeignKeys
، ولكن يمكن استخدامه بطرق أكثر تعقيدًا لوصف العلاقات بين المجموعات. يحتوي هذا النوع على المعاملon_delete
لتحديد ما يحدث عند حذف السجل المرتبط به مثل قيمةmodels.SET_NULL
التي تضبط القيمة علىNULL
.
هناك العديد من الأنواع الأخرى من الحقول بما في ذلك حقول أنواع مختلفة من الأعداد (الأعداد الصحيحة الكبيرة والأعداد الصحيحة الصغيرة والأعداد العشرية) والقيم المنطقية وعناوين URL والعناوين الفرعية Slugs والمعرّفات الفريدة وغيرها من المعلومات المتعلقة بالوقت (المدة والوقت وغير ذلك)، ويمكنك الاطلاع على القائمة الكاملة في توثيق جانغو.
البيانات الوصفية Metadata
يمكنك التصريح عن البيانات الوصفية على مستوى النموذج من خلال التصريح عن الصنف class Meta
كما يلي:
class Meta: ordering = ['-my_field_name']
تتمثل إحدى الميزات الأكثر فائدة لهذه البيانات الوصفية في التحكم في الترتيب الافتراضي للسجلات المُعادة عند الاستعلام عن نوع النموذج، ويمكنك تطبيق ذلك من خلال تحديد ترتيب المطابقة في قائمة أسماء الحقول مع السمة ordering
كما وضّحنا سابقًا. يعتمد الترتيب على نوع الحقل، إذ تُفرَز الحقول المحرفية أبجديًا، بينما تُفرَز حقول التاريخ بترتيب زمني، كما يمكنك أن تسبق اسم الحقل بإشارة ناقص (-) لعكس ترتيب الفرز.
إذا اخترنا مثلًا فرز الكتب افتراضيًا كما يلي:
ordering = ['title', '-pubdate']
فستُفرَز الكتب أبجديًا حسب العنوان من A إلى Z، ثم حسب تاريخ النشر ضمن كل عنوان من الأحدث إلى الأقدم.
هناك سمة شائعة أخرى هي verbose_name
، وهي اسم مطوَّل للصنف بصيغة المفرد والجمع:
verbose_name = 'BetterName'
تسمح لك السمات المفيدة الأخرى بإنشاء وتطبيق أذونات وصول جديدة للنموذج (تُطبَّق الأذونات الافتراضية تلقائيًا)، أو السماح بالترتيب بناءً على حقل آخر، أو التصريح عن أن الصنف "مجرد Abstract"، أي صنف أساسي لا يمكنك إنشاء سجلات له، وستُشتَق بدلًا من ذلك لإنشاء نماذج أخرى.
تتحكم العديد من خيارات البيانات الوصفية الأخرى في قاعدة البيانات التي يجب استخدامها مع النموذج وكيفية تخزين البيانات، وهي مفيدة حقًا إذا كنت بحاجة إلى ربط نموذج مع قاعدة بيانات موجودة مسبقًا. يمكنك الاطلاع على القائمة الكاملة لخيارات بيانات النموذج الوصفية في توثيق جانغو.
التوابع Methods
يمكن أن يحتوي النموذج أيضًا على توابع، إذ يجب عليك في كل نموذج على الأقل تعريف تابع لصنف بايثون المعياري __str__()
لإعادة سلسلة نصية يمكن يقرأها الإنسان لكل كائن، إذ تُستخدَم هذه السلسلة النصية لتمثيل السجلات الفردية في موقع المدير وفي أيّ مكان آخر تحتاج فيه للإشارة إلى نسخة من النموذج. يعيد هذا التابع غالبًا حقل العنوان أو الاسم من النموذج.
def __str__(self): return self.field_name
هناك تابع آخر شائع لتضمينه في نماذج جانغو وهو التابع get_absolute_url()
الذي يعيد عنوان URL لعرض سجلات النماذج على موقع الويب. إذا حدّدتَ هذا التابع، فسيضيف جانغو تلقائيًا زر "عرض على الموقع View on Site" إلى شاشات تعديل سجل النموذج في موقع المدير. يوضّح ما يلي نمطًا معياريًا للتابع get_absolute_url()
:
def get_absolute_url(self): """Returns the URL to access a particular instance of the model.""" return reverse('model-detail-view', args=[str(self.id)])
ملاحظة: يجب إنشاء رابط Mapper عنوان URL لتمرير الاستجابة والمعرّف إلى "عرض النموذج التفصيلي" (الذي سينفّذ العمل المطلوب لعرض السجل) بافتراض أنك ستستخدم عناوين URL، مثل "/myapplication/mymodelname/2" لعرض سجلات نموذجك، إذ يمثل "2" معرّف id
سجل معين. الدالة reverse()
السابقة قادرة على عكس رابط عنوان URL (المسماة في الحالة المذكورة سابقًا باسم نموذج-تفاصيل-عرض 'model-detail-View') لإنشاء عنوان URL بالتنسيق الصحيح، ويجب عليك كتابة ربط عنوان URL والعرض والقالب.
يمكنك تعريف أيّ توابع أخرى تريدها واستدعاؤها من شيفرتك البرمجية أو قوالبك بشرط ألّا تأخذ أيّ معاملات.
إدارة النموذج
يمكنك استخدام أصناف نموذجك بعد تعريفها لإنشاء السجلات أو تحديثها أو حذفها ولتشغيل الاستعلامات للحصول على جميع السجلات أو مجموعات فرعية معينة من السجلات. سنوضّح لك كيفية تطبيق ذلك لاحقًا عندما نعرّف عروضنا، ولكن إليك ملخصًا موجزًا.
إنشاء وتعديل السجلات
يمكنك إنشاء سجل من خلال تعريف نسخة من النموذج ثم استدعاء الدالة save()
.
# أنشِئ سجلًا جديدًا باستخدام باني النموذج record = MyModelName(my_field_name="Instance #1") # احفظ الكائن في قاعدة البيانات record.save()
ملاحظة: إذا لم تصرّح عن أي حقل primary_key
، فسيُمنَح السجل الجديد حقلًا من هذا النوع تلقائيًا مع معرّف id
اسم الحقل، إذ يمكنك الاستعلام عن هذا الحقل بعد حفظ السجل السابق، وسيكون له القيمة 1. يمكنك الوصول إلى الحقول في هذا السجل الجديد باستخدام الصيغة النقطية وتغيير القيم، ويجب عليك استدعاء الدالة save()
لتخزين القيم المُعدَّلة في قاعدة البيانات.
# الوصول إلى قيم حقل النموذج باستخدام سمات بايثون print(record.id) # يجب أن تعيد القيمة 1 للسجل الأول print(record.my_field_name) # يجب أن تطبع 'Instance #1' # غيّر السجل من خلال تعديل الحقول ثم استدعِ save() record.my_field_name = "New Instance Name" record.save()
البحث عن السجلات
يمكنك البحث عن السجلات التي تطابق معايير معينة باستخدام سمة النموذج objects
، والتي يوفّرها الصنف الأساسي.
ملاحظة: يمكن أن يكون شرح كيفية البحث عن السجلات باستخدام أسماء الحقول والنماذج المجردة مربكًا قليلًا، إذ سنشير في المناقشة الآتية إلى النموذج Book
باستخدام الحقلين title
و genre
، بحيث يكون النوع genre
نموذجًا له حقل name
واحد.
يمكننا الحصول على جميع سجلات النموذج بوصفها كائن QuerySet
باستخدام الدالة objects.all()
، إذ يُعَد QuerySet
كائنًا تكراريًا، مما يعني أنه يحتوي على عدد من الكائنات الممكن تكرارها.
all_books = Book.objects.all()
تسمح لنا الدالة filter()
من جانغو بترشيح الكائن QuerySet
المُعاد لمطابقة نص محدّد، أو حقل عددي مع معايير معينة، فمثلًا يمكننا تطبيق ما يلي لترشيح الكتب التي تحتوي على الكلمة "wild" في عنوانها ثم عَدّها:
wild_books = Book.objects.filter(title__contains='wild') number_wild_books = wild_books.count()
تُعرَّف الحقول المراد مطابقتها ونوع التطابق في اسم معامل الترشيح باستخدام التنسيق field_name__match_type
، ويمكنك ملاحظة الشرطة السفلية المزدوجة بين title
و contains
. رشّحنا title
باستخدام مطابقة حساسة لحالة الأحرف، وهناك العديد من أنواع التطابقات الأخرى الممكن إجراؤها، مثل icontains
(غير حساسة لحالة الأحرف) و iexact
(مطابقة تامة غير حساسة لحالة الأحرف) و exact
(مطابقة تامة حساسة لحالة الأحرف) و in
و gt
(أكبر من) و startswith
وغير ذلك. يمكنك الاطلاع على القائمة الكاملة في توثيق جانغو.
ستحتاج في بعض الحالات إلى ترشيح حقل يحدّد علاقة واحد إلى متعدد مع نموذج آخر، مثل ForeignKey
، إذ يمكنك في هذه الحالة "فهرسة Index" الحقول ضمن النموذج المتعلق بها باستخدام شرطات سفلية مزدوجة إضافية، لذلك سيتعين عليك فهرسة الاسم name
من خلال الحقل genre
لترشيح كتب ذات نمط نوع معين كما يلي:
# Will match on: Fiction, Science fiction, non-fiction etc. books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')
ملاحظة: يمكنك استخدام الشرطين السفليتين __
للتنقل عبر العديد من مستويات العلاقات ForeignKey
/ManyToManyField
كما تريد، فمثلًا يمكن أن يكون للكتاب Book
الذي له أنواع مختلفة ومُعرَّف باستخدام علاقة "cover" معامل اسمٍ هو type__cover__name__exact='hard'
.
هناك الكثير من الأشياء الممكن فعلها باستخدام الاستعلامات، مثل عمليات البحث العكسية من النماذج ذات الصلة وتسلسل المرشّحات وإعادة مجموعة أصغر من القيم وغير ذلك. اطلع على إجراء الاستعلامات في توثيق جانغو لمزيد من المعلومات.
تعريف نماذج المكتبة المحلية LocalLibrary
سنبدأ في هذا القسم بتعريف نماذج المكتبة، لذا افتح الملف "models.py" ضمن المجلد "/locallibrary/catalog/". تستورد الشيفرة البرمجية المتداولة الموجودة في أعلى الصفحة الوحدة models
التي تحتوي على صنف النموذج الأساسي models.Model
الذي سترثه نماذجنا.
from django.db import models # أنشِئ نماذجك هنا
النموذج Genre
انسخ شيفرة النموذج Genre
البرمجية التالية والصقه في نهاية الملف "models.py" الخاص بك. يُستخدَم هذا النموذج لتخزين المعلومات حول فئة الكتاب مثل كونه خياليًا أو غير خيالي، أو عاطفيًا أو تاريخًا وغير ذلك. أنشأنا النوع genre بوصفه نموذجًا وليس نصًا حرًا أو قائمة اختيار بحيث يمكن إدارة القيم الممكنة عبر قاعدة البيانات بدلًا من أن تكون شيفرة ثابتة.
class Genre(models.Model): """Model representing a book genre.""" name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)') def __str__(self): """String for representing the Model object.""" return self.name
يحتوي النموذج على حقل واحد من النوع CharField
هو name
، بحيث يُستخدَم لوصف نوع الكتاب وهو مُحدَّد ليحتوي على 200 محرف ولديه بعض نصوص التعليمات help_text
. صرّحنا في نهاية النموذج عن التابع __str__()
الذي يعيد اسم النوع الذي يحدّده سجل معين. لم يُحدَّد أيّ اسم مطوَّل لذلك سيُسمَّى الحقل بالاسم Name
في الاستمارات.
النموذج Book
انسخ النموذج Book
التالي والصقه في نهاية ملفك، إذ يمثل هذا النموذج جميع المعلومات حول الكتاب المتاح بالمعنى العام، وليس مثيلًا أو نسخةً ماديةً معينة متاحة للإعارة. يستخدم النموذجُ النوعَ CharField
لتمثيل الحقلين title
و isbn
الخاصَين بالكتاب، ولاحظ كيف يضبط المعامل الأول غير المُسمَّى للحقل isbn
التسميةَ بصورة صريحة على القيمة "ISBN" وإلّا فستُضبَط افتراضيًا على القيمة "Isbn". ضبطنا المعامل unique
على القيمة true
لضمان حصول جميع الكتب على رقم ISBN فريد، إذ يجعل المعامل unique
قيمة الحقل فريدةً بصورة عامة في الجدول. يستخدم النموذج النوع TextField
للحقل summary
، لأن هذا النص يمكن أن يكون طويلًا جدًا.
# يُستخدَم لإنشاء عناوين URL من خلال عكس أنماط عنوان URL from django.urls import reverse class Book(models.Model): """Model representing a book (but not a specific copy of a book).""" title = models.CharField(max_length=200) # اُستخدِم المفتاح الخارجي لأنه لا يمكن أن يكون للكتاب سوى مؤلف واحد، ولكن يمكن أن يكون للمؤلفين عدة كتب # المؤلف هو سلسلة نصية وليس كائنًا بسبب عدم النصريح عنه بعد في الملف author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True) summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book') isbn = models.CharField('ISBN', max_length=13, unique=True, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>') # استُخدم حقل ManyToManyField لأن النوع genre يمكن أن يحتوي على العديد من الكتب، ويمكن أن تغطي الكتب العديد من الأنواع. # عُرِّف الصنف Genre، وبالتالي يمكننا تحديد الكائن السابق. genre = models.ManyToManyField(Genre, help_text='Select a genre for this book') def __str__(self): """String for representing the Model object.""" return self.title def get_absolute_url(self): """Returns the URL to access a detail record for this book.""" return reverse('book-detail', args=[str(self.id)])
يكون الحقل genre
من النوع ManyToManyField
، بحيث يمكن أن يكون للكتاب أنواع متعددة ويمكن أن يحتوي النوع على العديد من الكتب. صُرِّح عن المؤلف على أنه من النوع ForeignKey
، لذلك سيكون لكل كتاب مؤلف واحد فقط، ولكن يمكن أن يكون للمؤلف العديد من الكتب. يمكن أن يكون للكتاب مؤلفِين متعددين عمليًا، ولكن هذا غير ممكن في هذا التطبيق.
يُصرَّح عن صنف النموذج ذي الصلة في كلا هذين النوعين من الحقول بوصفه أول معامل غير مسمًى باستخدام صنف النموذج أو سلسلة نصية تحتوي على اسم النموذج المرتبط بها، إذ يجب استخدام اسم النموذج بوصفه سلسلة نصية إذا لم يُعرَّف الصنف المرتبط به في هذا الملف قبل الإشارة إليه. المعاملات الأخرى ذات الأهمية في حقل المؤلف author
هي null=True
الذي يسمح لقاعدة البيانات بتخزين قيمة Null
عند عدم تحديد أي مؤلف، و on_delete=models.SET_NULL
الذي سيحدد قيمة حقل مؤلف الكتاب إلى Null
عند حذف سجل المؤلف المرتبط به.
تحذير: يكون المعامل on_delete=models.CASCADE
افتراضيًا، مما يعني أنه إذا حُذِف المؤلف، فسيُحذَف هذا الكتاب أيضًا. استخدمنا SET_NULL
هنا، ولكن يمكننا أيضًا استخدام PROTECT
أو RESTRICT
لمنع حذف المؤلف أثناء استخدام أيّ كتاب له.
يعرّف النموذج أيضًا التابعَ __str__()
باستخدام حقل عنوان الكتاب title
لتمثيل سجل Book
، إذ يعيد التابع الأخير get_absolute_url()
عنوان URL يمكن استخدامه للوصول إلى سجل تفصيلي لهذا النموذج، إذ يجب لتحقيق ذلك تعريف ربط لعنوان URL له الاسم book-detail
وتعريف عرض وقالب مرتبط به.
النموذج BookInstance
انسخ النموذج BookInstance
الآتي بعد النماذج الأخرى، إذ يمثل هذا النموذج نسخةً محددةً من كتاب يمكن أن يستعيره شخص ما، ويتضمن معلومات حول ما إذا كانت النسخة متاحة، أو التاريخ المتوقع لاسترجاعها وتفاصيل الطبعة، أو الإصدار ومعرّف فريد للكتاب في المكتبة.
ستصبح بعض هذه الحقول والتوابع مألوفةً الآن، إذ يستخدم النموذج BookInstance
ما يلي:
-
حقلًا من النوع
ForeignKey
لتحديد النموذجBook
المرتبط به (يمكن أن يكون لكل كتاب نسخ متعددة، ولكن يمكن أن يكون للنسخة كتابBook
واحد فقط). يحدِّد المفتاحon_delete=models.RESTRICT
لضمان عدم إمكانية حذف النموذجBook
عندما يشير إليه النموذجBookInstance
. -
حقلًا من النوع
CharField
لتمثيل الطبعة (إصدار محدد) للكتاب.
import uuid # مطلوب لنسخ الكتاب الفريدة class BookInstance(models.Model): """Model representing a specific copy of a book (i.e. that can be borrowed from the library).""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library') book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True) imprint = models.CharField(max_length=200) due_back = models.DateField(null=True, blank=True) LOAN_STATUS = ( ('m', 'Maintenance'), ('o', 'On loan'), ('a', 'Available'), ('r', 'Reserved'), ) status = models.CharField( max_length=1, choices=LOAN_STATUS, blank=True, default='m', help_text='Book availability', ) class Meta: ordering = ['due_back'] def __str__(self): """String for representing the Model object.""" return f'{self.id} ({self.book.title})'
نصرّح إضافةً إلى ذلك عن بعض الأنواع الجديدة من الحقول وهي:
-
يُستخدَم النوع
UUIDField
للحقلid
لضبطه بوصفه مفتاحًا رئيسيًاprimary_key
لهذا النموذج. يخصِّص هذا النوع من الحقول قيمةً فريدةً عامة لكل نسخة (قيمة لكل كتاب تجده في المكتبة). -
يُستخدَم النوع
DateField
للتاريخdue_back
، أي التاريخ المتوقع ليصبح فيه الكتاب متاحًا بعد استعارته أو صيانته، إذ يمكن أن تكون القيمةblank
أوnull
(مطلوبة عندما يكون الكتاب متاحًا). تستخدم بيانات النموذج الوصفية (Class Meta
) هذا الحقل لترتيب السجلات عند إعادتها ضمن استعلام. -
الحقل
status
من النوعCharField
الذي يعرّف قائمة الاختيار أو الخيارات. نعرّف كما ترى صفًا يحتوي على صفوف من أزواج مفتاح-قيمة ونمرّرها إلى وسيط الاختيارات. تُعَد القيمة في زوج مفتاح/قيمة قيمة عرض display value يمكن للمستخدم تحديدها، بينما تكون المفاتيح هي القيم المحفوظة عند تحديد الخيار. ضبطنا قيمة افتراضية هي "m" (للصيانة Maintenance) عند إنشاء الكتب في البداية إذ تكون غير متوفرة قبل تخزينها على الرفوف.
يمثل التابع __str__()
كائن BookInstance
باستخدام مجموعة من المعرّف الفريد وعنوان الكتاب Book
المرتبط به.
ملاحظة: إليك بعض المعلومات عن لغة بايثون:
- يمكنك بدءًا من الإصدار 3.6 للغة بايثون استخدام صيغة توليد السلاسل النصية (المعروفة أيضًا باسم f-strings) بالشكل التالي:
f'{self.id} ({self.book.title})'
-
استخدمنا سابقًا صيغة السلاسل النصية المُنسَّقة Formatted String التي تُعَد طريقةً صالحةً لتنسيق السلاسل النصية في لغة بايثون، مثل
'{0} ({1})'.format(self.id,self.book.title)
.
النموذج Author
انسخ نموذج Author
التالي بعد الشيفرة البرمجية الموجودة في الملف models.py:
class Author(models.Model): """Model representing an author.""" first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) date_of_birth = models.DateField(null=True, blank=True) date_of_death = models.DateField('Died', null=True, blank=True) class Meta: ordering = ['last_name', 'first_name'] def get_absolute_url(self): """Returns the URL to access a particular author instance.""" return reverse('author-detail', args=[str(self.id)]) def __str__(self): """String for representing the Model object.""" return f'{self.last_name}, {self.first_name}'
يجب أن تكون جميع الحقول والتوابع مألوفةً الآن، إذ يعرِّف هذا النموذج مؤلفًا يحمل اسمًا أول واسم عائلة وتاريخ ميلاد ووفاة (كلاهما اختياري)، ويحدّد أنّ التابع __str__()
افتراضيًا يعيد الاسم بالترتيب: اسم العائلة last name ثم الاسم الأول firstname. يعكس التابع get_absolute_url()
ربط عنوان URL لتفاصيل المؤلف author-detail
للحصول على عنوان URL لعرض مؤلف واحد.
إعادة تشغيل عمليات تهجير قاعدة البيانات
أنشأتَ حتى الآن جميع نماذجك، لذا أعِد تشغيل عمليات تهجير قاعدة البيانات لإضافتها إلى قاعدة بياناتك كما يلي:
python3 manage.py makemigrations python3 manage.py migrate
النموذج Language- تحدي
تخيل أن أحد المتبرعين المحليين يتبرع بعدد من الكتب الجديدة المكتوبة بلغة أخرى (الفارسية مثلًا)، إذ يكمن التحدي في معرفة أفضل طريقة لتمثيلها في موقع مكتبتنا ثم إضافتها إلى النماذج.
إليك بعض الأشياء التي يجب مراعاتها:
-
هل يجب ربط اللغة بالنموذج
Book
أوBookInstance
أو أيّ كائن آخر؟ - هل يجب تمثيل اللغات المختلفة باستخدام نموذج، أم حقل نص حر، أم قائمة اختيار ثابتة؟
أضف الحقل بعد أن تقرر ما تريده، ويمكنك أن ترى ما قررناه على GitHub.
لا تنسَ أنه يجب إعادة تشغيل عمليات تهجير قاعدة البيانات مرة أخرى لإضافة التغييرات بعد إجراء تغيير على نموذجك.
python3 manage.py makemigrations python3 manage.py migrate
الخلاصة
تعلمنا في هذا المقال كيفية تعريف النماذج، ثم استخدمنا هذه المعلومات لتصميم وتقديم النماذج المناسبة لموقع المكتبة المحلية LocalLibrary.
سنحوّل الآن اهتمامنا عن إنشاء الموقع لفترة وجيزة لنتعرّف على موقع إدارة جانغو الذي سيسمح لنا بإضافة بعض البيانات إلى المكتبة، والتي يمكننا عرضها بعد ذلك باستخدام العروض والقوالب التي لم ننشئها بعد.
ترجمة -وبتصرُّف- للمقال Django Tutorial Part 3: Using models.
اقرأ أيضًا
- المقال التالي: تطبيق عملي لتعلم جانغو - الجزء الثالث: موقع مدير جانغو Django Admin
- المقال السابق: تطبيق عملي لتعلم إطار عمل جانغو - الجزء الأول: إنشاء موقع ويب هيكلي لمكتبة محلية
- كتابة شيفرات بايثون: صيغ شائعة الاستخدام على نحو خاطئ
- إنشاء تطبيق جانغو وتوصيله بقاعدة بيانات
- النماذج Models والاستعلام عن البيانات في جانغو
- كتابة أول تطبيق جانغو - الجزء 2 (توثيق جانغو)
- إجراء الاستعلامات (توثيق جانغو)
- مرجع واجهة برمجة تطبيقات QuerySet (توثيق جانغو)
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.