تحدثنا في الدرسين السابقين عن كيفية تنصيب وإعداد إطار العمل Django على أنظمة التشغيل المختلفة، وتعرفنا كذلك على مفهومي المشروع والتطبيق، وقمنا بكتابة العرض الأول وتعرفنا بشكل مختصر على المساراتUrls.
وفي الجزء الثالث من هذه السلسلة، سنتطرق إلى النماذج Models وكيفية التعامل مع قواعد البيانات من خلال Django، وسنتعرف خلال الدرس على طريقة ربط قواعد البيانات المختلفة مع Django، وكيفية إنشاء النماذج والتعامل معها وسنتطرق كذلك إلى مفهوم تهجير قواعد البيانات Migration، وسننشئ النموذج الخاص بتطبيق الاقتراعات والذي سيحتضن الأسئلة الخاصة بالاقتراع إضافة إلى الإجابات المرتبطة بالسؤال.
النماذج Models
أشرنا في الدروس السابقة إلى أن إطار العمل Django يتبع مبدأ العمل MVT، ويشير الحرف M هنا إلى النماذج Models، ويمكن تعريف النماذج بشكل مبسط على أنها وصف للبيانات الموجودة في قاعدة البيانات باستخدام لغة Python، وبمعنى آخر تمثّل النماذج في Django بنية قاعدة البيانات، أي ما ستحصل عليه من إجراء الأمر CREATE TABLE
ولكن بلغة Python بدلًا من SQL.
يستخدم Django النماذج في التواصل مع قواعد البيانات من خلال تنفيذ أوامر SQL خلف الكواليس وتقديم النتائج التي ترد من هذه الأوامر على هيئة بنية من البيانات تمثل الصفوف في جداول قاعدة البيانات.
ولهذا الأسلوب في التعامل مع قواعد البيانات بعض الفوائد، فكتابة النماذج بلغة Python يزيد في الانتاجية، إذ لن يكون المبرمج مضطرًا إلى اتباع قواعد لغة أخرى أثناء العمل على التطبيق ويكون العمل مقتصرًا على لغة python، كما يسهل هذا الأسلوب متابعة النماذج وتتبع التغييرات الحاصلة عليها من خلال أنظمة تتبع الإصدارات كـ Git وغيرها، إضافة إلى ذلك تقدم نماذج Python بعض أنواع البيانات غير المتوفرة في SQL كالبريد الإلكتروني وعناوين URL وغيرها.
ربط Django مع قواعد البيانات
يستطيع Django التعامل مع أنواع مختلفة من أنظمة قواعد البيانات، ومن أشهرها SQLite، MySQL، PostgreSQL و Oracle. وفي مشروعنا هذا سنستخدم قواعد بيانات SQLite والتي تعتبر الخيار الأبسط والأسهل في حالة المشاريع البسيطة والصغيرة ويتم تثبيتها عند تثبيت Python وهذا يعني عدم الحاجة إلى تثبيت أي حزم إضافية، كما أنها ليست بحاجة إلى خادوم خاص تعمل من خلاله.
يمكن اختيار نوع قاعدة البيانات التي سيعمل عليها المشروع من خلال ملف الإعدادات settings.py، وذلك ضمن القاموس DATABASES.
يستخدم Django قواعد بيانات SQLite بشكل افتراضي، وستجد قاموس DATABASES بالشكل التالي:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
يتطلب ربط Django بقواعد البيانات الأخرى كـ MySQL و PostgreSQL تثبيت حزم الربط الخاصة بكل نوع منها، وإضافة العناصر ‘USER’، ‘PASSWORD’، و’HOST’ إلى القاموس، ويمكن التعرف إلى الحزم المطلوبة لكل نوع من أنواع قواعد البيانات في توثيقات Django.
إنشاء النموذج الأول
سيعتمد تطبيق الاقتراعات على نموذجين فقط هما نموذج الأسئلة ونموذج الإجابات، وسيتضمن النموذج الأول السؤال وتاريخ نشره، أما النموذج الثاني فسيتضمن الإجابات ومجموع الأصوات على كل إجابة، وسترتبط كل إجابة بسؤال معين.
افتح ملف models.py
في محرر النصوص المفضل لديك، ثم أضف الأسطر التالية:
class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') class Choice(models.Model): question = models.ForeignKey(Question) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
يمكن أن نلاحظ أن كل نموذج في هذا الملف قد تم تمثيله بصنف والذي يمثل بدوره جدولًا في قاعدة البيانات، ويتضمن كل صنف عددًا من المتغيرات التي يمكن استخدامها في شيفرة Python حسب الحاجة إليها، كما تمثل اسم العمود في قاعدة البيانات.
تمثل أسماء المتغيرات التي استخدمناها في الشيفرة السابقة أسماء الأعمدة في قواعد البيانات، وسنستخدم هذه الأسماء في شيفرة Python وستظهر كذلك في لوحة التحكم التي سنتحدث عنها مفصّلًا في الدروس القادمة، ويمكن التحكم في طريقة عرض هذه الأسماء في لوحة التحكم وذلك باستخدام معامل يجب تعيين قيمته قبل أي معامل آخر، كما فعلنا مع المتغير pub_date
أعلاه، وبهذه الطريقة سيظهر هذا الحقل في لوحة التحكم بالاسم date published
وليس pub_date
.
نلاحظ في الشيفرة السابقة أيضًا أننا عرّفنا علاقة تربط بين الإجابات وبين السؤال الخاص بها، وذلك باستخدام ForeignKey
والتي تخبر Django بأن كل إجابة ترتبط بسؤال واحد فقط.
تمتلك بعض أنواع الصنف Field
عددًا من المعاملات الإلزامية، كما هو الحال مع CharField
والذي يتطلب تحديد العدد الأقصى للحروف من خلال max_length
. وبطبيعة الحالة تمتلك هذه الأصناف بعض المعاملات الاختيارية، كما هو الحال مع IntegerField
الذي أضفنا إليه قيمة افتراضية مساوية للصفر من خلال default=0
.
يتم تمثيل كل حقل من حقول قاعدة البيانات بـ instance للصنف Field
، مثل CharField
لحقول الحروف، و DateTimeField
لحقول التاريخ والوقت.
يقدّم Django عددًا كبيرًا ومتنوعًا من أنواع الحقول، نورد فيما يلي بعضًا منها:
الحقل | الوظيفة |
BooleanField | يتضمن قيمتي True و False، وعادة ما يكون مرتبطًا بمربع الاختيار Checkbox. |
CharField | يستخدم هذا الحقل لإضافة السلاسل النصية Strings القصيرة والمتوسطة الحجم. |
DateField | يستخدم للتعبير عن التاريخ. |
DateTimeField | يستخدم للتعبير عن التاريخ الوقت. |
DecimalField | يتيح استخدام الأرقام العشرية التي تحتوي على فواصل. |
EmailField | عبارة عن حقل حرفي `CharField` ولكنه قادر على التحقق من أن القيمة المدخلة تمتلك صيغة بريد إلكتروني سليمة. |
FileField | يستخدم هذا الحقل لاحتواء الملفات المرفوعة. |
IntegerField | يتيح استخدام الأرقام الصحيحة، ويمكن استخدام الأرقام من -2147483648 إلى 2147483647 بشكل آمن مع أنظمة قواعد البيانات المختلفة. |
GenericIPAddressField | عبارة عن عنوان IPv4 أو IPv6 على هيئة سلسلة نصية. |
SlugField | حقل Slug. |
TextField | يستخدم في لإضافة السلاسل النصية الطويلة. |
TimeField | يستخدم للتعبير عن الوقت. |
URLField | عبارة عن حقل حرفي يتضمن عنوان URL. |
تفعيل النموذج
تستطيع الشيفرة السابقة القيام بأمور كثيرة، حيث يمكن لـ Django أن:
-
ينشئ جدولًا جديدًا خاصًّا بتطبيقنا هذا وذلك بتنفيذ العبارة
CREATE TABLE
. - إنشاء واجهة برمجية API خاصة يمكن من خلالها التعامل مع قاعدة البيانات التي تم إنشاؤها.
ولكن قبل أن يشرع Django بأداء هذه المهام، يجب علينا تثبيت تطبيق polls في مشروعنا وذلك من خلال إدراج اسمه ضمن قائمة INSTALLED_APPS
في ملف الإعدادات settings.py
، لذا توجه إلى هذا الملف وابحث عن القائمة التي تحمل الاسم INSTALLED_APPS
، ثم عدل عناصرها لتصبح بالشكل التالي:
-
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls', ]
توجّه الآن إلى مجلد المشروع عن طريق سطر الأوامر ونفّذ الأمر التالي:
python manage.py makemigrations polls
يفترض أن ترى النتيجة التالية في سطر الأوامر:
Migrations for 'polls': polls/migrations/0001_initial.py: - Create model Choice - Create model Question - Add field question to choice
وظيفة الأمر makemigrations
هي إخبار Django بأنّك قد أجريت بعض التعديلات على النماذج وأنّك ترغب في حفظ هذه التعديلات على هيئة ملف تهجير يُحفظ على القرص الصلب كملف بايثون في مجلد migrations
، تحت اسم: 0001_initial.py
.
والآن نفّذ الأمر التالي في سطر الأوامر وذلك لتنفيذ التهجيرات وإنشاء الجداول ضمن قاعدة البيانات:
python manage.py migrate
يفترض أن تظهر النتيجة التالية في سطر الأوامر:
Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Rendering model states... DONE Applying polls.0001_initial... OK
تتلخص وظيفة الأمر migrate
في البحث عن التهجيرات غير المنفّذة (يتابع Django التهجيرات غير المنفّذة من خلال جدول خاص في قاعدة البيانات يحمل الاسم django_migrations
) وإجرائها على قاعدة البيانات، سواء أكانت هذه التهجيرات تتضمن إنشاء جداول جديدة أو تعديل وتحديث جداول موجودة بالفعل.
إذًا يمكن تلخيص عملية إجراء التعديلات على النماذج بالخطوات الثلاثة التالية:
-
إجراء التعديلات على النموذج في ملف
models.py
. -
تنفيذ الأمر
python manage.py makemigrations
لإنشاء التهجيرات المرتبطة بالتعديلات التي تم إجراؤها على النموذج. -
تنفيذ الأمر
python manage.py migrate
لتطبيق هذه التعديلات على قاعدة البيانات.
الواجهة البرمجية الخاصة بالتعامل مع قاعدة البيانات
ذكرنا سابقًا بأن لدى Django واجهة برمجية للتعامل مع قاعدة البيانات، وأفضل طريقة للتعرف على هذه الواجهة هي التعامل المباشر معها من خلال سطر الأوامر وعن طريق صدفة Python.
للولوج إلى الصدفة نفّذ الأمر التالي في سطر الأوامر:
python manage.py shell
أولًا يجب استيراد صنفي النموذج Question و Choice، كما سنحتاج إلى حزمة timezone
من مكتبة django.utils
للتعامل مع الوقت والتاريخ:
from polls.models import Question, Choice from django.utils import timezone
يمكن الاستعلام عن جميع الأسئلة الموجودة في جدول Question من خلال الشيفرة:
Question.objects.all()
وباعتبار أن قاعدة البيانات الخاصة بتطبيقنا هذا خالية تمامًا من الأسئلة، فسنحصل على النتيجة:
<QuerySet []>
لإنشاء السؤال الأول سنقوم بإنشاء كائن من الصنف Question مع تعريف قيم المعاملات المستخدمة في هذا الصنف وإسناد هذا الكائن إلى متغير، وكما يلي:
q = Question(question_text="What's new?", pub_date=timezone.now())
يمكن الآن حفظ السؤال الجديد في قاعدة البيانات من خلال الشيفرة التالية:
q.save()
والآن، يمكن الوصول إلى جميع المعلومات الخاصة بهذا السؤال وبالشكل التالي:
#الحصول على نص السؤال q.question_text #الحصول على تاريخ نشر السؤال q.pub_date # تغيير نص السؤال q.question_text = "What’s up?" q.save()
بعد أن أضفنا سؤالًا إلى قاعدة البيانات، سنجري استعلامًا عن جميع الأسئلة في النموذج Question:
Question.objects.all() #ستحصل على النتيجة التالية <QuerySet [<Question: Question object>]>
نلاحظ أن النتيجة التي حصلنا عليها غريبة بعض الشيء، فالسؤال الذي أضفناه قبل قليل لم يظهر ضمن قائمة الأسئلة، ولحل هذه المشكلة سنحتاج إلى إضافة تابع __str()__
إلى كلا الصنفين، وبالشكل التالي:
from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible # إن كنت بحاجة إلى استخدام الإصدار الثاني من بايثون class Question(models.Model): # ... def __str__(self): return self.question_text @python_2_unicode_compatible # إن كنت بحاجة إلى استخدام الإصدار الثاني من بايثون class Choice(models.Model): # ... def __str__(self): return self.choice_text
من الضروري استخدام التابع __str__()
عند التعامل مع النماذج، لأن نتيجة هذا التابع ستظهر في لوحة التحكم التي يتم إنشائها آليًا من خلال هذه النماذج.
وكما قمنا باستخدام أحد التوابع المعرفة مسبقًا في Django فبمقدورنا أن نستخدم توابع مخصّصة تؤدي وظائف ومهام مختلفة، ولتوضيح ذلك، قم باستيراد مكتبة datetime
الخاصة بـ Python، ثم أضف الشيفرة التالية في نهاية الصنف Question:
import datetime # ... class Question(models.Model): # ... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
سيقوم هذا التابع بالتحقق من أن السؤال قد تم نشره مؤخّرًا (منذ يوم) أو منذ فترة بعيدة (أكثر من يوم).
والآن يمكن استدعاء تابعنا المخصص الجديد بنفس الطريقة التي استدعينا من خلالها تابع الحفظ:
q.was_published_recently()
افتح الآن صدفة بايثون جديدة، واستورد الأصناف Question و Choice بنفس الطريقة السابقة، ثم نفذ شيفرة الاستعلام عن جميع الأسئلة، ولاحظ الفرق.
طرق الاستعلام عن البيانات في Django
لا شك أن التطبيقات على أرض الواقع تتضمن قواعد بيانات تحتوي على عدد هائل من البيانات والمعلومات، وهذا يتطلب وسيلة للاستعلام عنها حسب شرط معين، ويقدم Django طرقًا متعددة لإجراء استعلامات مخصصة وبأساليب مختلفة.
يقدّم Django ثلاثة توابع خاصة بالصنف QuerySet
هي: filter()
, exclude()
, get()
، وجملة الاستعلام عبارة عن معاملات مفتاحية Keyword arguments
لهذه التوابع، وتأخذ الصيغة التالية: الحقل__نوع الاستعلام = القيمة.
يقوم كل تابع من هذه التوابع الثلاثة بإنشاء كائن جديد للصنف QuerySet
يتضمن النتائج التي تم
الحصول عليها بعد إجراء الاستعلام.
يقوم التابع filter()
بإرجاع جميع العناصر المتوافقة مع الاستعلام، أما التابع exclude()
فيرجع جميع العناصر غير المتوافقة مع الاستعلام، أما التابع get()
فيرجع العنصر الذي يطابق جملة الاستعلام، وفي حالة العثور على أكثر من عنصر واحد يقوم هذا التابع بإطلاق الاستثناء MultipleObjectsReturned
وإن لم يعثر على أي عنصر فسيطلق الاستثناء DoesNotExist
.
إليك بعض الأمثلة التي توضح طريقة عمل هذه التوابع:
يمكن البحث عن السؤال الذي يبدأ بعبارة معينة بالشكل التالي:
Question.objects.filter(question_text__startswith='What')
ستترجم هذه الشيفرة إلى عبارة SQL التالية:
SELECT … WHERE question_text LIKE ‘What%’;
ويمكن البحث عن سؤال معين من خلال المعرّف الخاص به، فللبحث عن السؤال ذي المعرف رقم 1، يمكن كتابة الشيفرة:
Question.objects.filter(id=1)
والتي تعادل عبارة SQL التالية:
SELECT … WHERE id = 1;
مثال على التابع exclude():
Question.objects.exclude(pub_date__gt=datetime.date(2016, 1, 3), question_text = "What’s up?")
تترجم هذه الشيفرة إلى عبارة SQL التالية:
SELECT … WHERE NOT (pub_date > ‘2016-1-3’ AND question_text = “What’s up?”)
يوضح الجدول التالي بعض أنواع الاستعلامات التي يقدّمها Django:
نوع الاستعلام | الوظيفة |
exact | تطابق تام. |
iexact | تطابق تام مع تجاهل حالة الأحرف. |
contains | البحث عن كلمة مع أخذ حالة الأحرف بنظر الاعتبار. |
icontains | البحث عن كلمة مع تجاهل حالة الأحرف. |
in | البحث ضمن قائمة معينة. |
gt | أكبر من |
gte | أكبر من أو يساوي |
lt | أصغر من |
lte | أصغر من أو يساوي |
startswith | يبدأ بـ |
istartswith | يبدأ بـ مع أخذ حالة الأحرف بنظر الاعتبار. |
endswith | ينتهي بـ |
iendswith | ينتهي بـ مع أخذ حالة الأحرف بنظر الاعتبار. |
range | البحث ضمن مدى معين |
date | البحث عن تاريخ معين |
year | البحث عن سنة معينة |
month | البحث عن شهر معين |
day | البحث عن يوم معين |
week_day | البحث عن أسماء أيام الأسبوع |
hour | البحث عن ساعة |
minute | البحث عن دقيقة |
second | البحث عن ثانية |
لنستعلم الآن عن السؤال الذي يحمل المفتاح الرئيسي primary key
رقم 1 في قاعدة البيانات الخاصة بنا، وإسناد النتيجة إلى متغير:
q = Question.objects.get(pk=1)
يمكننا الآن الوصول إلى مجموعة الأجوبة choice_set
والتي ينشئها Django تلقائيًا عند ربط الأسئلة بالأجوبة من خلال ForeignKey
.
q.choice_set.all()
بطبيعة الحال، فإن مجموعة الأجوبة الخاصة بسؤالنا هذا فارغة، لذا سنقوم بإنشاء بعض الأجوبة وإضافتها إلى هذه المجموعة:
q.choice_set.create(choice_text = 'Not much', votes = 0) q.choice_set.create(choice_text = 'The sky', votes = 0) c = q.choice_set.create(choice_text = 'Just hacking again', votes = 0)
والآن يمكن التعرف على السؤال المرتبط بالجواب:
c.question
ويمكن معرفة عدد الأجوبة المتاحة لسؤال معين عن طريق التابع count()
:
q.choice_set.count()
ويمكن حذف إجابة معينة عن طريق التابع delete()
:
c.delete()
خاتمة
تعرفنا في هذا الدرس على العنصر الأول من عناصر إطار العمل Django ألا وهو النماذج Models، وتعرفنا كذلك على طريقة التعامل مع قواعد البيانات من خلال هذه النماذج وكيفية تهجيرها والاستعلام عن البيانات برمجيًا.
أما الدرس القادم فسيكون عن المسارات URLS وآلية عملها، وكيفية استخدام التعابير النظامية Regular Expressions في إنشائها.
المصدر:
توثيقات Django.
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.