نشرح في مقال اليوم بنية نموذج-قالب-عرض Model-Template-View أو MTV اختصارًا التي يعتمد عليها إطار عمل جانغو Django لتطوير الويب حيث يكون النموذج Model مسؤولًا عن التفاعل مع قاعدة البيانات، ويجب أن يقابل كل نموذج جدولًا منها، ويعبر القالب Template عن جزء الواجهة الأمامية من التطبيق، وهو الشيء الذي سيراه المستخدم، أما العرض View فهو يتضمن المنطق البرمجي لواجهة التطبيق الخلفية، ومسؤولً عن استرداد البيانات من قاعدة البيانات عبر النماذج ووضعها في العرض المقابل وإعادة القالب المعروض إلى المستخدم في النهاية.
هذه المقالة جزء من سلسلة من المقالات تشرح جانغو للمبتدئين على النحو التالي:
- الجزء الأول: البدء في إنشاء مدونة بسيطة
- الجزء الثاني: استخدام بنية MTV لإنشاء مدونة بسيطة
- الجزء الثالث: استخدام عمليات CRUD لإدارة المدونة
- الجزء الرابع: تطبيق المدونة الكامل
- الجزء الخامس: إضافة بعض الميزات المتقدمة إلى تطبيق المدونة
مفهوم النماذج Models
النموذج من أفضل ميزات جانغو Django، ففي أطر عمل الويب الأخرى ستحتاج إلى إنشاء نموذج وملف تهجير Migration، وملف التهجير هو مخطط Schema لقاعدة البيانات، يصف بنية قاعدة البيانات كأسماء الأعمدة وأنواعها، ويوفر النموذج واجهةً تتعامل مع معالجة البيانات بناءً على هذا المخطط، ولكنك ستحتاج إلى نموذج فقط في جانغو، ويمكن توليد ملفات التهجير المقابلة باستخدام أمر بسيط هو python manage.py makemigrations
، مما يوفر عليك كثيرًا من الوقت.
يحتوي كل تطبيق جانغو على ملف models.py
واحد، ويجب تعريف جميع النماذج المرتبطة بالتطبيق بداخله، ويقابل كل نموذج ملف تهجير، والذي يقابل بدوره جدولًا في قاعدة البيانات. يمكنك التمييز بين الجداول الخاصة بالتطبيقات المختلفة من خلال إسناد بادئة لكل جدول تلقائيًا، حيث سيكون لجدول قاعدة البيانات المقابل لتطبيق blog
الخاص بنا البادئة blog_
.
يوضح المثال التالي نموذجًا في الملف blog/models.py
:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
يمكنك بعد ذلك توليد ملف التهجير باستخدام الأمر التالي:
python manage.py makemigrations
ويجب أن يبدو ملف التهجير الناتج blog/migrations/0001_initial.py
كما يلي:
# Generated by Django 4.1.2 on 2022-10-19 23:13 from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name="Person", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("first_name", models.CharField(max_length=30)), ("last_name", models.CharField(max_length=30)), ], ), ]
يمكنك اعتبار ملف التهجير بمثابة مخطط يوضح كيف يجب أن يبدو جدول قاعدة البيانات، ويمكنك استخدام الأمر التالي لتطبيق هذا المخطط:
python manage.py migrate
يجب أن تبدو قاعدة بياناتك db.sqlite3
كما يلي:
لا تحاول تعديل أو حذف ملفات التهجير إذا كنت مبتدئًا، ودع جانغو يطبّق كل شيء نيابةً عنك إلّا إن كنت تعرف ما تفعله تمامًا. يمكن لجانغو في معظم الحالات اكتشاف التغييرات التي أجريتها في النماذج حتى إذا حذفتَ شيئًا وتوليد ملفات الترحيل وفقًا لذلك.
سينشئ النموذج في مثالنا جدول قاعدة بيانات person
، وسيحتوي هذا الجدول على ثلاثة أعمدة اسمها id
و first_name
و last_name
، حيث يُنشأ العمود id
تلقائيًا كما هو موضح في ملف التهجير، ويُستخدم هذا العمود كمفتاح رئيسي Primary Key للفهرسة Indexing افتراضيًا.
يسمَّىCharField()
بنوع الحقل ويعرِّف نوع العمود، ويسمَّى max_length
بخيار الحقل ويحدّد معلومات إضافية حول هذا العمود.
أهم نواع حقول النموذج وخياراتها
يوضّح الجدول التالي بعض أنواع الحقول الأكثر استخدامًا وننصحك بالاطلاع على جميع أنواع الحقول وخياراتها من توثيق جانغو الرسمي.
نوع الحقل | وصف عنه |
---|---|
BigAutoField
|
ينشئ عمودًا نوعه عدد صحيح يتزايد تلقائيًا، يُستخدم عادةً مع العمود id .
|
BooleanField
|
ينشئ عمودًا قيمه منطقية True أو False .
|
DateField و DateTimeField
|
يُستخدم لإضافة تواريخ وأوقات كما يوحي اسمه. |
FileField و ImageField
|
ينشئ عمودًا لتخزين المسار الذي يؤشّر إلى الملف أو الصورة المرفوعة. |
IntegerField و BigIntegerField
|
تتراوح قيم الأعداد الصحيحة Integer من -2147483648 إلى 2147483647، وتتراوح قيم الأعداد الصحيحة الكبيرة Big Integer من -9223372036854775808 إلى 9223372036854775807 |
SlugField
|
الاسم المختصر Slug هو نسخة بسيطة من عنوان URL للاسم أو العنوان. |
CharField و TextField
|
ينشئ كل من CharField و TextField عمودًا لتخزين السلاسل النصية Strings، ولكن يقابل TextField مربع نص أكبر في صفحة مدير جانغو Django Admin التي سنتحدث عنها لاحقًا في هذه السلسلة من المقالات.
|
يوضح الجدول التالي بعض خيارات الحقول الأكثر استخدامًا:
خيار الحقل | وصف عنه |
---|---|
blank
|
يسمح للحقل بأن يحتوي على إدخال فارغ. |
choices
|
يمنح الحقل خيارات متعددة، حيث سنوضّح ذلك لاحقًا عندما نصل إلى مدير جانغو. |
default
|
يعطي الحقل قيمةً افتراضية. |
unique
|
يتأكد من أن كلّ عنصر في العمود فريد، ويُستخدَم عادةً لتحديد الاسم المختصر Slug والحقول الأخرى التي يُفترَض أن تحتوي على قيم فريدة. |
خيارات الصنف Meta
يمكنك أيضًا إضافة الصنف Class الذي هو Meta
في صنف النموذج، والذي يحتوي على معلومات إضافية حول هذا النموذج مثل اسم جدول قاعدة البيانات وخيارات الترتيب والأسماء المفردة وجمعها التي يمكن أن يقرأها الإنسان كما يلي:
class Category(models.Model): priority = models.IntegerField() class Meta: ordering = ["priority"] verbose_name_plural = "categories"
توابع النموذج
توابع النموذج هي دوال معرَّفة في صنف النموذج، تسمح بتطبيق إجراءات مخصصة على النسخة الحالية من كائن النموذج كما في المثال التالي:
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): "Returns the person's baby-boomer status." import datetime 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"
في الكود السابق، يفحص جانغو تاريخ ميلاد الشخص ويعيد حالة تمثل إن كانت فترة ولادته قبل زيادة المواليد التي حدثت بعد الحرب العالمية الثانية Pre-boomer، أم خلالها Baby-Boomer أم بعدها Post-boomer للشخص عند استدعاء التابع baby_boomer_status()
.
ملاحظة: الكائنات والتوابع والخاصيات مفاهيم مهمة جدًا في لغات البرمجة، لذا ننصح بمطالعة مقال كائنات وأصناف بايثون لمزيد من المعلومات.
وراثة النماذج
ستحتاج إلى أكثر من نموذج واحد في معظم تطبيقات الويب، وسيكون لبعضها حقول مشتركة، لذا يمكنك إنشاء نموذج أب Parent Model يحتوي على الحقول المشتركة، ثم تجعل النماذج الأخرى ترث هذا النموذج الأب كما يلي:
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
نموذج مجرد Abstract، مما يعني أنه لا يقابل نموذجًا فرديًا فعليًا، بل يستخدم كأب لنماذج أخرى.
لنولّد الآن ملف تهجير جديد blog/migrations/0002_student.py
باستخدام الأمر التالي للتحقق من ذلك:
python manage.py makemigrations
سيتولّد ملف التهجير التالي:
# Generated by Django 4.1.2 on 2022-10-19 23:28 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("blog", "0001_initial"), ] operations = [ migrations.CreateModel( name="Student", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("name", models.CharField(max_length=100)), ("age", models.PositiveIntegerField()), ("home_group", models.CharField(max_length=5)), ], options={ "abstract": False, }, ), ]
لاحظ إنشاء الجدول Student
فقط.
علاقات قاعدة البيانات
تحدثنا عن كيفية إنشاء جداول فردية، ولكن لا تكون هذه الجداول مستقلة تمامًا في معظم التطبيقات، بل توجد علاقات بينها، فمثلًا قد يكون لديك فئة Category تنتمي إليها منشورات متعددة، ويكون كل منشور في المدونة تابع لمستخدم معين وما إلى ذلك، لذا توجد ثلاثة أنواع أساسية للتعبير عن العلاقات بين جداول قاعدة البيانات وهي: علاقة واحد إلى واحد One-to-one وعلاقة متعدد إلى واحد Many-to-one وعلاقة متعدد إلى متعدد Many-to-many وسنشرح تاليًا المزيد حول هذه العلاقات وطريقة وصفها ضمن نماذج جانغو.
علاقة واحد إلى واحد
علاقة واحد إلى واحد هي العلاقة الأسهل، فمثلًا يمكن أن يكون لكل شخص هاتف واحد، ويمكن أن يعود كل هاتف إلى شخص واحد، حيث يمكننا وصف هذه العلاقة في النماذج كما يلي:
class Person(models.Model): name = models.CharField(max_length=100) class Phone(models.Model): person = models.OneToOneField('Person', on_delete=models.CASCADE)
يعمل نوع الحقل OneToOneField
مثل أي نوع حقل آخر، ولكنه يتطلب وسيطين على الأقل هما: الوسيط الأول هو اسم النموذج الآخر الذي يرتبط به هذا النموذج بعلاقة، والوسيط الثاني هو on_delete
الذي يعرّف الإجراء الذي سيتخذه جانغو عند حذف البيانات، ويتعلق هذا المعامل بلغة SQL أكثر من جانغو، لذا لن نتحدث عنه بالتفصيل، ولكن إن كنت مهتمًا، فاطلع على بعض القيم المتاحة للمعامل on_delete
.
يمكنك الآن إنشاء عمليات تهجير وتطبيقها لهذه النماذج ومعرفة ما يحدث، وإذا واجهتك مشاكل أثناء تشغيل الأوامر التالية، فاحذف الملف db.sqlite3
وملفات التهجير للبدء من جديد:
python manage.py makemigrations
python manage.py migrate
لاحظ أن نوع الحقل OneToOneField
أنشأ العمود person_id
في الجدول blog_phone
، وسيخزّن هذا العمود معرّف id
الشخص الذي يمتلك هذا الهاتف.
علاقة متعدد إلى واحد
يمكن أن تحتوي كل فئة على منشورات متعددة وينتمي كل منشور إلى فئة واحدة مثلًا، حيث يشار إلى هذه العلاقة باسم علاقة متعدد إلى واحد التي نعرّفها كما يلي:
class Category(models.Model): name = models.CharField(max_length=100) class Post(models.Model): category = models.ForeignKey('Category', on_delete=models.CASCADE)
ينشئ نوع الحقل ForeignKey
العمود category_id
في الجدول blog_post
، والذي يخزن معرّف id
الفئة التي ينتمي إليها هذا المنشور.
علاقة متعدد إلى متعدد
تكون علاقة متعدد إلى متعدد أكثر تعقيدًا بعض الشيء، فمثلًا يمكن أن يكون لكل مقال وسوم Tags متعددة، ويمكن أن يكون لكل وسم مقالات متعددة:
class Tag(models.Model): name = models.CharField(max_length=100) class Post(models.Model): tags = models.ManyToManyField('Tag')
ستنشئ الشيفرة البرمجية السابقة جدولًا جديدًا بالاسم post_tags
بدلًا من إنشاء عمود جديد، وسيحتوي هذا الجدول الجديد على عمودين هما post_id
و tag_id
، مما يتيح لك تحديد جميع الوسوم المرتبطة بمنشور معين والعكس صحيح.
لنفترض أن لدينا الجدول التالي مثلًا لتوضيح الأمور:
post_id | tag_id |
---|---|
1 | 1 |
2 | 1 |
3 | 2 |
1 | 2 |
2 | 3 |
يمتلك المنشور الذي له المعرّف id=1
وسمين tags لهما المعرّف id=1
والمعرّف id=2
. إذا أردنا عكس الأمور للعثور على المنشورات باستخدام الوسم، فيمكننا أن نرى منشورين لهما المعرّف id=3
والمعرّف id=1
بالنسبة للوسم ذي المعرّف id=2
.
طبقة العرض View
طبقة العرض مكون مهم في تطبيق جانغو ، فهي المكان الذي نكتب فيه المنطق البرمجي للواجهة الخلفية. يسترد العرض البيانات من قاعدة البيانات من خلال النموذج المقابل ويعالج هذه البيانات المستردة ويضعها في الموقع المقابل في القالب ويعرض هذا القالب ويعيده إلى المستخدم.
لا تعمل دالة العرض View Function على استرداد البيانات فقط، إذ توجد أربع عمليات أساسية يمكن تطبيقها على البيانات في معظم تطبيقات الويب، وهي الإنشاء Create والقراءة Read والتحديث Update والحذف Delete، ويشار إلى هذه العمليات مجتمعة بالاسم CRUD، والتي سنوضّحها في مقال لاحق.
تحدّثنا عن النماذج في القسم السابق، ولكننا ما زلنا لا نعرف كيفية استرداد البيانات أو تخزينها باستخدام النموذج، حيث يقدم جانغو واجهة برمجة تطبيقات API بسيطة لمساعدتنا في ذلك، تسمى QuerySet
.
لنفترض أن لدينا النموذج blog/models.py
التالي:
class Category(models.Model): name = models.CharField(max_length=100) class Tag(models.Model): name = models.CharField(max_length=200) class Post(models.Model): title = models.CharField(max_length=255) content = models.TextField() pub_date = models.DateField() category = models.ForeignKey(Category, on_delete=models.CASCADE) tags = models.ManyToManyField(Tag)
يمكنك معالجة البيانات بمساعدة QuerySet
من خلال هذا النموذج ضمن دوال العرض التي توجد في الملف blog/views.py
.
إنشاء البيانات وحفظها
لنفترض أنك تريد إنشاء فئة جديدة كما يلي:
# استيراد نموذج الفئة Category from blog.models import Category # إنشاء نسخة جديدة من النموذج Category category = Category(name="New Category") # حفظ الفئة التي أنشأناها في قاعدة البيانات category.save()
يجب أن يكون ما سبق سهلًا إذا كنت على دراية بمفهوم البرمجة كائنية التوجه، حيث أنشأنا في المثال السابق نسخة جديدة من الكائن Category
واستخدمنا التابع save()
الذي ينتمي إلى هذا الكائن لحفظ المعلومات في قاعدة البيانات.
توجد علاقة متعدد إلى واحد بين الفئة والمنشور، وقد عرفناها باستخدام الحقل ForeignKey()
، مع افتراض أن لدينا عدد كافي من السجلات في قاعدة البيانات.
from blog.models import Category, Post # Post.objects.get(pk=1) هي الطريقة التي نسترجع بها المنشور الذي له المفتاح الرئيسي pk=1، # حيث يرمز pk إلى المفتاح الرئيسي Primary Key الذي يمثّل المعرّف id عادةً إن لم نحدّد خلاف ذلك. post = Post.objects.get(pk=1) # استرداد الفئة التي اسمها "New Category" new_category = Category.objects.get(name="New Category") # إسناد المتغير new_category إلى حقل فئة المنشور وحفظه post.category = new_category post.save()
توجد أيضًا علاقة متعدد إلى متعدد بين المنشورات والوسوم كما يلي:
from blog.models import Tag, Post post1 = Post.objects.get(pk=1) # استرداد المنشور 1 tag1 = Tag.objects.get(pk=1) # استرداد الوسم 1 tag2 = Tag.objects.get(pk=2) # استرداد الوسم 2 tag3 = Tag.objects.get(pk=3) # استرداد الوسم 3 tag4 = Tag.objects.get(pk=4) # استرداد الوسم 4 tag5 = Tag.objects.get(pk=5) # استرداد الوسم 5 post.tags.add(tag1, tag2, tag3, tag4, tag5) # إضافة الوسوم من 1 إلى 5 إلى المنشور 1
استرداد البيانات
استرداد الكائنات أكثر تعقيدًا قليلًا مما رأيناه سابقًا، لذا سنوضّح فيما يلي كيف سنجد سجلًا معينًا في قاعدة بياناتنا التي تحتوي على آلاف السجلات إن لم نكن نعرف المعرّف id
، وسنوضّح كيفية الحصول على مجموعة من السجلات التي تناسب معايير معينة بدلًا من الحصول على سجل واحد مثلًا.
توابع QuerySet
تتيح توابع QuerySet
استرداد البيانات بناءً على معايير معينة، ويمكن الوصول إليها باستخدام السمة Attribute التي هي objects
، حيث يُستخدَم التابع get()
الذي رأيناه سابقًا لاسترداد سجل معين كما يلي:
first_tag = Tag.objects.get(pk=1) new_category = Category.objects.get(name="New Category")
ويمكن أيضًا استرداد جميع السجلات باستخدام التابع all()
:
Post.objects.all()
يعيد التابع all()
مجموعة من السجلات والتي نسميها مجموعة الاستعلام QuerySet، ويمكنك تحسين هذه المجموعة من خلال سَلسَلة التابع filter()
أو التابع exclude()
مع التابع all()
كما يلي:
Post.objects.all().filter(pub_date__year=2024)
سيؤدي ذلك إلى إعادة جميع المنشورات المنشورة في عام 2024، ويُسمَّى pub_date__year
بوسيط البحث في الحقول Field Lookup، وسنناقش هذا الموضوع بالتفصيل لاحقًا.
يمكننا أيضًا استبعاد المنشورات المنشورة عام 2024 كما يلي:
Post.objects.all().exclude(pub_date__year=2024)
هناك أيضًا العديد من توابع QuerySet الأخرى بالإضافة إلى get()
و all()
و filter()
و exclude()
، ولكن لن نتحدث عنها جميعًا، لذا اطّلع على القائمة الكاملة لجميع توابع QuerySet من توثيق جانغو الرسمي.
وسطاء عمليات البحث في الحقول Field Lookups
وسطاء عمليات البحث في الحقول هي وسطاء الكلمات المفتاحية للتوابع get()
و filter()
و exclude()
، والتي تعمل بطريقة مشابهة لتعليمة WHERE
في لغة SQL، وتأخذ الصيغة fieldname__lookuptype=value
مع وجود شرطة سفلية مزدوجة.
Post.objects.all().filter(pub_date__lte='2024-01-01')
pub_date
هو اسم الحقل و lte
هو نوع البحث الذي يرمز إلى أقل من أو يساوي، وستُعاد جميع المنشورات التي يكون فيها تاريخ النشر pub_date
أقل من أو يساوي 2024-01-01
.
يمكن أيضًا استخدام وسطاء عمليات البحث في الحقول للعثور على السجلات التي لها علاقة بالسجل الحالي كما في المثال التالي، وستُعاد جميع المنشورات التي تنتمي إلى الفئة التي اسمها "Django"
:
Post.objects.filter(category__name='Django')
يمكننا تطبيق ذلك عكسيًا مثل إعادة جميع الفئات التي تحتوي على منشورٍ واحد على الأقل الذي يحتوي عنوانه على الكلمة "Django"
كما يلي:
Category.objects.filter(post__title__contains='Django')
يمكننا أيضًا المرور عبر علاقات متعددة كما يلي:
Category.objects.filter(post__author__name='Admin')
يعيد الاستعلام هنا جميع الفئات التي تحتوي على منشورات ينشرها المستخدم Admin
، إذ يمكن وضع سلسلة من العلاقات بالعدد الذي نريده. ويمكنك مطالعة جميع وسطاء عمليات البحث في الحقول التي يمكنك استخدامها من توثيق جانغو الرسمي.
حذف الكائنات
نستخدم التابع delete()
لحذف سجلٍ ما، حيث ستحذف الشيفرة البرمجية التالية المنشور الذي له المفتاح الرئيسي pk=1
:
post = Post.objects.get(pk=1) post.delete()
ويمكننا أيضًا استخدامه لحذف سجلات متعددة معًا كما يلي:
Post.objects.filter(pub_date__year=2022).delete()
سيؤدي ذلك إلى حذف جميع المنشورات في عام 2022، ولكن قد يتعلّق السجل الذي نحذفه بسجل آخر مثل محاولة حذف فئة تحتوي على منشورات متعددة كما يلي:
category = Category.objects.get(pk=1) category.delete()
يحاكي جانغو سلوك قيد SQL التالي ON DELETE CASCADE
، مما يعني حذف جميع المنشورات التي تنتمي إلى هذه الفئة أيضًا، ولكن إذا أردتَ تغيير ذلك، فيمكنك تغيير خيار on_delete
إلى شيء آخر، لذا اطّلع على جميع خيارات on_delete
المتاحة في توثيق جانغو الرسمي.
دالة العرض View Function
وضّحنا ما يمكن فعله داخل دالة العرض، وسنوضّح كيف تبدو دالة العرض الكاملة كما في المثال التالي، حيث تُعرَّف جميع العروض ضمن الملف views.py
:
from django.shortcuts import render from blog.models import Post # أنشئ عروضك الخاصة هنا def my_view(request): posts = Post.objects.all() return render(request, 'blog/index.html', { 'posts': posts, })
هناك شيئان يجب الانتباه إليهما في هذا المثال، أولهما أن دالة العرض تأخذ المتغير request
كدخل، وهذا المتغير هو كائن HttpRequest
يُمرَّر تلقائيًا إلى العرض من موجّه إرسال Dispatcher عناوين URL. اطّلع على مقال مدخل إلى HTTP لمزيد من المعلومات حول التواصل بين عميل وخادم التطبيق.
يحتوي request
على الكثير من المعلومات حول طلب HTTP الحالي، فمثلًا يمكننا الوصول إلى تابع طلب HTTP وكتابة شيفرات برمجية مختلفة لتوابع مختلفة كما يلي:
if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else()
ملاحظة: اطّلع على جميع المعلومات التي يمكنك الوصول إليها من كائن request
في توثيق جانغو الرسمي.
والشيء الآخر الذي يجب الانتباه إليه هو استيراد اختصار Shortcut اسمه render()
، ثم استخدامه لتمرير المتغير posts
إلى القالب blog/index.html
. يُطلق عليه اختصار لأنه من المفترض تحميل القالب افتراضيًا باستخدام التابع loader()
وعرض هذا القالب مع البيانات المُسترَدة وإعادة كائن HttpResponse
، ولكن بسّط جانغو هذه العملية باستخدام الاختصار render()
. لن نتحدث عن الطريقة المعقدة لذلك، لأننا لن نستخدمها في هذا المقال ويمكنك مطالعة جميع دوال الاختصار Shortcut Functions في توثيق جانغو الرسمي.
نظام قوالب جانغو
طبقة القوالب Template هي جزء الواجهة الأمامية لتطبيق جانغو، لذا تكون ملفات القالب هي شيفرات مكتوبة بلغة HTML لأنها تمثّل ما تراه في المتصفح، ولكن قد تكون الأمور أكثر تعقيدًا قليلًا، فإن احتوى القالب على شيفرات HTML فقط، فسيكون موقع الويب ساكنًا بالكامل، ولا نريد ذلك، لذا يجب أن يخبر القالبُ دالةَ العرض بمكان وضع البيانات المسترَدة.
عمليات الضبط Configurations
يجب أولًا تغيير شيء ما في الملف settings.py
، إذ يجب أن تخبر جانغو بمكان وضع ملفات القوالب، لذا لننشئ المجلد templates
، حيث اخترنا وضعه ضمن المجلد الجذر للمشروع، ولكن يمكنك نقله لمكان آخر.
. ├── blog ├── db.sqlite3 ├── djangoBlog ├── env ├── manage.py ├── mediafiles ├── staticfiles └── templates
انتقل إلى الملف settings.py
وابحث عن TEMPLATES
.
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ 'templates', ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
لنعدّل الخيار DIRS
الذي يؤشر إلى المجلد templates
، ولنتحقق الآن من عمل هذا الإعداد، لذا ننشئ نمط عنوان URL جديد يؤشّر إلى العرض test()
في الملف djangoBlog/urls.py
كما يلي:
from django.urls import path from blog import views urlpatterns = [ path('test/', views.test), ]
ولننشئ الآن العرض test()
في الملف blog/views.py
كما يلي:
def test(request): return render(request, 'test.html')
انتقل إلى المجلد templates
وأنشئ القالب test.html
كما يلي:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Test Page</title> </head> <body> <p>This is a test page.</p> </body> </html>
شغّل خادم التطوير وانتقل إلى العنوان http://127.0.0.1:8000/
، وستظهر الصفحة التالية:
لغة قوالب جانغو
لنناقش الآن محرّك قوالب جانغو بالتفصيل، وتذكّر أنه يمكننا إرسال البيانات من العرض إلى القالب كما يلي:
def test(request): return render(request, 'test.html', { 'name': 'Jack' })
تُسنَد السلسلة النصية 'Jack'
إلى المتغير name
وتمرَّر إلى القالب، ويمكننا عرض المتغير name
ضمن القالب باستخدام أقواس معقوصة مزدوجة {{ }}
كما يلي:
<p>Hello, {{ name }}</p>
حدّث المتصفح، وسترى النتيجة التالية:
ولكن لا تكون البيانات المُمرَّرة إلى القالب سلسلة نصية بسيطة في أغلب الحالات كما في المثال التالي:
def test(request): post = Post.objects.get(pk=1) return render(request, 'test.html', { 'post': post })
المتغير post
في المثال السابق هو قاموس Dictionary، حيث يمكنك الوصول إلى العناصر الموجودة في هذا القاموس في القالب كما يلي:
{{ post.title }} {{ post.content }} {{ post.pub_date }}
المرشحات Filters
تحوّل المرشحات قيم المتغيرات، فمثلًا إذا كان لدينا المتغير django
الذي قيمته 'the web framework for perfectionists with deadlines'
ووضعنا المرشح title
مع هذا المتغير كما يلي:
{{ django|title }}
فسيُحوَّل القالب إلى ما يلي:
The Web Framework For Perfectionists With Deadlines
اطّلع على جميع المرشحات المُضمَّنة في جانغو من توثيق جانغو الرسمي.
الوسوم Tags
تضيف الوسوم ميزات لغات البرمجة مثل التحكم في التدفق والحلقات إلى شيفرة HTML، مما يوفر الكثير من الوقت والموارد، إذ لن نضطر إلى كتابة الشيفرة البرمجية نفسها مرارًا وتكرارًا. تُعرَّف جميع الوسوم باستخدام {% %}
مثل حلقة for
التالية:
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul>
وتكون تعليمة if
كما يلي:
{% if somevar == "x" %} This appears if variable somevar equals the string "x" {% endif %}
وتكون تعليمة if-else
كما في المثال التالي:
{% if athlete_list %} Number of athletes: {{ athlete_list|length }} {% elif athlete_in_locker_room_list %} Athletes should be out of the locker room soon! {% else %} No athletes. {% endif %}
اطّلع على جميع الوسوم المُضمَّنة في جانغو من توثيق جانغو الرسمي، حيث توجد الكثير من المرشحات والوسوم المفيدة الأخرى في نظام قوالب جانغو، والتي سنتحدث عنها لاحقًا.
نظام الوراثة Inheritance
الفائدة الأساسية لاستخدام قوالب جانغو هي أنك لست بحاجة إلى كتابة الشيفرة البرمجية نفسها مرارًا وتكرارًا، فمثلًا يوجد شريط تنقل Navigation Bar وتذييل Footer في جميع صفحات تطبيق الويب النموذجي، ومن الصعب تكرار هذه الشيفرة البرمجية في كل صفحة من صيانة هذا التطبيق، لذا يقدم جانغو طريقة سهلة للغاية لحل هذه المشكلة كما سنوضح فيما يلي.
لننشئ الملف layout.html
في المجلد templates
، ويمثّل هذا الملف المكان الذي نعرّف فيه تخطيط القالب الخاص بنا كما يلي، ولكننا لم نضع الشيفرة البرمجية الخاصة بالتذييل وشريط التنقل لتسهيل القراءة:
<!DOCTYPE html> <html> <head> {% block meta %} {% endblock %} <-- استورد شيفرة CSS هنا –!> </head> <body> <div class="container"> <!-- ضع شيفرة شريط التنقل هنا --> {% block content %} {% endblock %} <!-- ضع شيفرة تذييل الصفحة هنا --> </div> </body> </html>
لاحظ أننا عرّفنا كتلتين في هذا الملف هما meta
و content
باستخدام الوسم {% block ... %}
، وعرّفنا القالب home.html
التالي لاستخدام هذا التخطيط:
{% extends 'layout.html' %} {% block meta %} <title>Page Title</title> <meta charset="UTF-8"> <meta name="description" content="Free Web tutorials"> <meta name="keywords" content="HTML, CSS, JavaScript"> <meta name="author" content="John Doe"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> {% endblock %} {% block content %} <p>This is the content section.</p> {% include 'vendor/sidebar.html' %} {% endblock %}
سيجد جانغو أولًا الملف layout.html
عند استدعاء القالب السابق، ويملأ الكتلتين meta
و content
بالمعلومات الموجودة في صفحة home.html
.
لاحظ وجود شيء آخر في هذا القالب، حيث تخبر التعليمة {% include 'vendor/sidebar.html' %}
جانغو بالبحث عن القالب templates/vendor/sidebar.html
ووضعه في الصفحة، حيث يتضمن القالب sidebar.html
المحتويات التالية مثلًا:
<p>This is the sidebar.</p>
لا يُعَد ذلك شريطًا جانبيًا، ولكن يمكننا استخدامه لتوضيح عمل نظام الوراثة، وتأكد أيضًا من صحة العرض كما يلي:
from django.shortcuts import render def home(request): return render(request, 'home.html')
وتأكّد من أن موجّه إرسال عنوان URL الخاص بك يؤشّر إلى هذا العرض كما يلي:
path('home/', views.home),
افتح متصفحك وانتقل إلى العنوان http://127.0.0.1:8000/home
، ويجب أن ترى الصفحة التالية:
ترجمة -وبتصرّف- للمقال Django for Beginners #2 - The MTV Structure لصاحبه Eric Hu.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.