نحتاج عادةً إلى قاعدة بيانات في تطبيقات الويب، وهي مجموعة مُنطمّة من البيانات، نستخدمها لتخزين وتنظيم البيانات الدائمة، موفّرةً لنا إمكانية استرجاع هذه البيانات ومعالجتها بفعالية، إذ نحتاج مثلًا في تطبيق ما للتواصل الاجتماعي إلى قاعدة بيانات لتخزين بيانات المستخدم (من معلومات شخصية ومنشورات وتعليقات ومتابعين) بحيث نتمكّن من معالجة البيانات هذه بفعالية، إذ توفّر قاعدة البيانات إمكانية إضافة بيانات جديدة إليها، أو استرجاع بيانات مُخزّنة أصلًا، أو التعديل عليها، أو حذفها بكل مرونة وذلك اعتمادًا على المتطلبات والشروط والظروف المُختلفة، ففي تطبيق ويب ما قد تكون هذه المتطلبات مثلًا هي حالة إضافة المُستخدم لمنشور جديد، أو حذف منشور سابق، أو حذف حسابه مع كافّة منشوراته أو مع الإبقاء عليها، بمعنى أنّ طريقة معالجة البيانات تعتمد أولًا وأخيرًا على الميزات التي يتيحها التطبيق، فمثلًا قد لا ترغب بالسماح لمُستخدمي تطبيقك بإضافة منشورات غير معنونة.
يُعد فلاسك إطار عمل للويب مبني بلغة بايثون، ويتميز بكونه صغير الحجم وسهل المعالجة، ويوفّر أيضًا عدة أدوات وميزات من شأنها إنشاء تطبيقات ويب في لغة بايثون.
أمّا PostgreSQL أو Postgres فهو نظام لإدارة قواعد البيانات العلاقية، ويوفّر تطبيقًا للغة الاستعلامات SQL، ومتوافق مع معاييرها، ناهيك عن امتلاكه العديد من الميزات المتطورة من عمليات موثوقة وتحكّم متزامن مُتعدّد النسخ دون قيود على قراءة البيانات من القاعدة.
سنعمل في هذا المقال على بناء تطبيق ويب مُصغّر لتقييم الكتب والذي سنوضّح من خلاله كيفية استخدام المكتبة psycopg2
ومحوّل قاعدة بيانات PostgreSQL، الذي يسمح لنا بالتخاطب والتفاعل مع قاعدة بيانات PostgreSQL باستخدام لغة بايثون، إذ سنبني التطبيق باستخدام إطار العمل فلاسك ليؤدّي بعض المهام الأساسية من اتصال بخادم قاعدة البيانات وإنشاء الجداول وإدخال بيانات في جدول، إضافةً إلى جلب بيانات من جدول.
مستلزمات العمل
قبل المتابعة في هذا المقال لا بُدّ من:
- توفُّر بيئة برمجة بايثون 3 محلية، مثبّتة على حاسوبك، وسنفترض في مقالنا أن اسم مجلد المشروع هو "flask_app".
- الفهم الجيد لأساسيات فلاسك، مثل مفهوم الوجهات ودوال العرض والقوالب، وفي هذا الصدد يمكنك الاطلاع على المقالين كيفية بناء موقعك الإلكتروني الأول باستخدام إطار عمل فلاسك Flask من لغة بايثون وكيفية استخدام القوالب في تطبيقات فلاسك Flask لفهم مبادئ فلاسك.
- فهم أساسيات لغة HTML.
- توفّر PostgreSQL مثبت على حاسوبك مع وصول إلى نافذة أسطر الأوامر الخاصة به.
الخطوة 1 - إنشاء كل من قاعدة بيانات ومستخدم PostgreSQL
سنعمل في هذه الخطوة على إنشاء قاعدة بيانات باسم "flask_db" ومستخدم لها باسم user
وذلك لاستخدامهما لاحقًا في تطبيق فلاسك المراد إنشاؤه.
يُنشأ خلال تثبيت postgres
مستخدم بصلاحيات على مستوى نظام التشغيل باسم postgres
ليتوافق مع حساب المستخدم المسؤول كامل الصلاحيات postgres
الخاص بنظام PostgreSQL، إذ سنستخدم هذا الحساب في المهام التي تتطلّب صلاحيات مسؤول، فحينها سنستخدم الأمر sudo
ممرّرين بعده اسم المستخدم مع استخدام الخيار iu-
.
لذا، سجّل دخولك الآن إلى جلسة عمل تفاعلية في Postgres مُستخدمًا الأمر التالي:
user@localhost:$ sudo -iu postgres psql
فستظهر لك طرفية PostgreSQL والتي من الممكن إعداد المتطلبات من خلالها.
بدايةً، سننشئ قاعدة بيانات لمشروعنا:
postgres=# CREATE DATABASE flask_db;
ملاحظة: يجب أن تنتهي كل تعليمة من تعليمات Postgres بفاصلة منقوطة، ففي حال ظهور أي أخطاء، تأكّد من انتهاء كل سطر برمجي بفاصلة منقوطة.
ومن ثمّ سننشئ حساب مستخدم لقاعدة البيانات، وهنا لا بدّ من اختيار كلمة مرور آمنة:
postgres=# CREATE USER user WITH PASSWORD 'password';
والآن سنعطي هذا المستخدم صلاحيات المسؤول على قاعدة البيانات:
postgres=# GRANT ALL PRIVILEGES ON DATABASE flask_db TO sammy;
وللتأكّد من إتمام إنشاء قاعدة البيانات، نكتب الأمر التالي المسؤول عن إظهار قائمة بقواعد البيانات المُنشأة:
postgres=# \l
فستظهر قاعدة البيانات المسماة "flask_db" ضمن القائمة السابقة.
وعند الانتهاء نغلق نافذة طرفية PstgreSQL باستخدام الأمر:
postgres=# \q
ومع نهاية هذه الخطوة نكون قد أعددنا sPostgre وأصبح من الممكن الاتصال وإدارة المعلومات المخزّنة في قواعد بياناته باستخدام لغة بايثون معتمدين على المكتبة psycopg2
، أمّا في الخطوة التالية فسنعمل على تثبيت هذه المكتبة جنبًا إلى جنب مع حزمة فلاسك.
الخطوة 2 - تثبيت فلاسك ومكتبة psycopg2
سنعمل في هذه الخطوة على تثبيت كل من فلاسك ومكتبة psycopg2
مما يمكننا من التخاطب مع قاعدة البيانات باستخدام لغة بايثون.
لذا، وبعد التأكّد من كون البيئة الافتراضية مُفعّلة، نستخدم أمر تثبيت الحزم pip
لتثبيت كل من فلاسك ومكتبة psycopg2
على النحو التالي:
(env)user@localhost:$ pip install Flask psycopg2-binary
وبمجرّد انتهاء التثبيت بنجاح، سيظهر في نهاية الخرج سطر شبيه بما يلي مؤكدًا نجاح العملية:
Successfully installed Flask-2.0.2 Jinja2-3.0.3 MarkupSafe-2.0.1 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1 psycopg2-binary-2.9.2
مع انتهاء هذه الخطوة، نكون قد ثبتنا كافّة الحزم اللازمة في البيئة الافتراضية، وسنعمل في الخطوة التالية على إعداد قاعدة البيانات والاتصال بها.
الخطوة 3 - إعداد قاعدة بيانات
سننشئ في هذه الخطوة -ضمن مجلد المشروع المسمّى "flask_app"- ملف بايثون وظيفته الاتصال مع قاعدة البيانات "flask_db"، كما سننشئ ضمن قاعدة البيانات هذه جدولًا لتخزين الكتب، وسندخل ضمنه يدويًا بعض الكتب مع نبذة عن كل منها.
الآن وبعد التأكّد من كون البيئة الافتراضية مُفعّلة، نفتح ملفًا جديدًا باسم "init_db.py" ضمن المجلد "flask_app" على النحو التالي:
(env)user@localhost:$ nano init_db.py
سيفتح هذا الملف الاتصال مع قاعدة البيانات "flask_db"، مُنشئًا فيها جدولًا للكتب باسم "books"، مالئًا إياه ببيانات تجريبية، ولإنجاز ذلك نكتب الشيفرة التالية ضمن الملف "init_db.py":
import os import psycopg2 conn = psycopg2.connect( host="localhost", database="flask_db", user=os.environ['DB_USERNAME'], password=os.environ['DB_PASSWORD']) # Open a cursor to perform database operations cur = conn.cursor() # Execute a command: this creates a new table cur.execute('DROP TABLE IF EXISTS books;') cur.execute('CREATE TABLE books (id serial PRIMARY KEY,' 'title varchar (150) NOT NULL,' 'author varchar (50) NOT NULL,' 'pages_num integer NOT NULL,' 'review text,' 'date_added date DEFAULT CURRENT_TIMESTAMP);' ) # Insert data into the table cur.execute('INSERT INTO books (title, author, pages_num, review)' 'VALUES (%s, %s, %s, %s)', ('A Tale of Two Cities', 'Charles Dickens', 489, 'A great classic!') ) cur.execute('INSERT INTO books (title, author, pages_num, review)' 'VALUES (%s, %s, %s, %s)', ('Anna Karenina', 'Leo Tolstoy', 864, 'Another great classic!') ) conn.commit() cur.close() conn.close()
نحفظ الملف ونغلقه.
استوردنا في الشيفرة السابقة الوحدة os
المُستخدمة في الوصول إلى متغيرات البيئة التي خزّنا فيها سابقًا كل من اسم المستخدم وكلمة المرور الخاصين بقاعدة البيانات، وهذا يضمن عدم ظهورهما ضمن الشيفرة المصدرية.
كما استوردنا المكتبة psycopg2
، ومن ثمّ فتحنا اتصالًا مع قاعدة البيانات flask_db باستخدام الدالة ()psycopg2.connect
، وفيها حددنا المضيف وهو الحاسوب المحلي في حالتنا، كما مرّرنا اسم قاعدة البيانات إلى المُعامل database
.
وقد وفرّنا كل من اسم المستخدم وكلمة المرور اللازمين للدخول إلى قاعدة البيانات باستخدام الكائن os.environ
الذي يُمكنّنا من الوصول إلى متغيرات البيئة التي أعددناها سابقًا ضمن البيئة البرمجية، ليُخزّن اسم المستخدم ضمن متغير بيئة باسم DB_USERNAME
وكلمة المرور ضمن متغير بيئة باسم DB_PASSWORD
، وبهذا نكون قد خزّنا كل من اسم المستخدم وكلمة المرور خارج الشيفرة المصدرية، ما يضمن أمان هذه المعلومات الحساسة ويمنع كشفها لدى حفظ نسخة من الشيفرة المصدرية في المتحكّم المركزي، أو لدى رفعها على خادم عامل على شبكة الإنترنت.
كما أنشأنا مؤشر cur
باستخدام التابع ()connection.cursor
، الذي يمكّن شيفرات بايثون من تنفيذ أوامر PostgreSQL خلال جلسات عمل قاعدة البيانات.
استخدمنا تابع المؤشر ()execute
لحذف أي جداول موجودةٍ مسبقًا باسم "books" في حال وجودها، وذلك لتجنُّب أي تضاربٍ، أو نتائج عشوائية ناتجةٍ عن تشابه أسماء الجداول (مثل حالة وجود جدولين بنفس الاسم وبأعمدة مُختلفة في قاعدة البيانات)، ولكن وفي حالتنا الآن وكوننا لم ننشئ أي جداول بعد، فلن يُنفَّذ هذا السطر في الوقت الراهن، ومن الجدير بالملاحظة أنّ هذا الأمر سيحذِف كل المحتويات في قاعدة البيانات في كل مرة تشغِّل فيها الملف "init_db.py"، ولكن في حالة مثالنا سننفذ هذا الملف لمرّة واحدة فقط لتهيئة قاعدة البيانات، إلّا أنّك قد ترغب مُستقبلًا بتنفيذه مُجدّدًا بغية تفريغ قاعدة البيانات من كافّة محتوياتها والبدء من جديد بقاعدة بيانات فارغة.
ثمّ استخدمنا الأمر CREATE TABLE books
لإنشاء جدول للكتب ضمن قاعدة البيانات باسم books
يحتوي على الأعمدة التالية:
-
'id': يحتوي على بياناتٍ من نمط رقم تسلسلي
serial
، أي أعداد صحيحة متزايدة تلقائيًا، ويمثّل هذا العمود مفتاحًا أساسيًا والذي قد حدّدناه باستخدام الكلمات المفتاحيةPRIMARY KEY
. وستُخصّص قاعدة البيانات قيمة فريدة لهذا المفتاح من أجل كل سجل مُدخل (والسجل هو الكتاب في حالتنا). -
"title": يمثّل عنوان الكتاب، ويحتوي على بيانات من النوع
varchar
، وهو نوع بيانات يحتوي على محارف بطول معين، وفي حالتنا(varchar (150
تعني أنّ عدد المحارف المتاحة للعنوان هي حتى 150 محرف، أمّا التعليمةNOT NULL
فتعني أنّه من غير المسموح ترك هذا العمود فارغًا. -
author
: يحتوي على اسم مؤلف الكتاب، على ألّا يتجاوز 50 محرف، أمّا التعليمةNOT NULL
فتعني أنّه من غير المسموح ترك هذا العمود فارغًا. -
pages_num
: يحتوي على عدد صحيح يمثّل عدد صفحات الكتاب، وتعني التعليمةNOT NULL
أنّه من غير المسموح ترك هذا العمود فارغًا. -
review
: يحتوي على نبذة عن الكتاب، وبياناته من النوعtext
، ما يعني أنّ محتويات هذا العمود هي سلاسل نصية بأي طول. -
'date_added': يحتوي على تاريخ إضافة الكتاب إلى الجدول، والقيمة
DEFAULT
تعني ضبط القيمة الافتراضية إلىCURRENT_TIMESTAMP
، التي تمثِّل وقت إضافة الكتاب إلى قاعدة البيانات، وكما هو الحال في عمودid
، لا يتوجب عليك تحديد قيم لهذا العمود، إذ أنها تُملأ تلقائيًا.
بعد الانتهاء من إنشاء الجدول، استخدمنا تابع المؤشّر ()execute
لإدخال كتابين في الجدول، الأول بعنوان "A Tale of Two Cities" للكاتب "Charles Dickens"، والثاني بعنوان "Anna Karenina" للمؤلف "Leo Tolstoy"، وقد استخدمنا الموضع المؤقت s%
لتمرير القيم إلى تعليمات SQL، إذ تتعامل مكتبة psycopg2
مع عمليات الإدخال في الخلفية بطريقة تضمن الحماية من هجمات حقن تعليمات SQL.
بعد الانتهاء من إدخال بيانات الكتب إلى الجدول، استخدمنا التابع ()connection.commit
لتأكيد العملية وتطبيق التغييرات على قاعدة البيانات، ونهايةً أغلقنا كل من المؤشر والاتصال باستخدام التابعين ()cur.close
و ()conn.close
على التوالي.
الآن، سنشغّل الأوامر التالية لتأسيس الاتصال مع قاعدة البيانات، وإعداد كل من متغيري البيئة DB_USERNAME
و DB_PASSWORD
، ولكن استخدم اسم المستخدم وكلمة المرور الخاصين بك:
(env)user@localhost:$ export DB_USERNAME="user" (env)user@localhost:$ export DB_PASSWORD="password"
والآن، سنشغّل الملف "init_db.py" ضمن نافذة الطرفية باستخدام الأمر python
كما يلي:
(env)user@localhost:$ python init_db.py
وفور انتهاء تنفيذ الملف بنجاح، سيُضاف جدول جديد باسم "books" إلى قاعدة البيانات "flask_db".
والآن سجّل الدخول إلى جلسة Postgres تفاعلية للتحقّق من الجدول الجديد المُسمّى "books"، على النحو التالي:
(env)user@localhost:$ sudo -iu postgres psql
نتصل مع قاعدة البيانات "flask_db" باستخدام الأمر c\
:
postgres=# \c flask_db
سنستخدم الآن التعليمة SELECT
للحصول على عناوين الكتب وأسماء مؤلفيها من جدول الكتب books
على النحو التالي:
postgres=# SELECT title, author FROM books;
فيظهر الخرج كما يلي:
title | author ----------------------+------------------ A Tale of Two Cities | Charles Dickens Anna Karenina | Leo Tolstoy
نُغلق الجلسة التفاعلية باستخدام الأمر q\
.
سنعمل في الخطوة التالية على إنشاء تطبيق فلاسك مُصغّر، يتصل بقاعدة البيانات ليجلب منها نبذةً عن كلا الكتابين اللذين أدخلناهما سابقًا في قاعدة البيانات ليعرضهما في صفحته الرئيسية.
الخطوة 4 - عرض الكتب
سنعمل في هذه الخطوة على إنشاء تطبيق فلاسك ذو صفحة رئيسية مسؤولة عن عرض الكتب الموجودة في قاعدة البيانات بعد جلبها منها.
لذا وأثناء كون البيئة البرمجية مُفعّلة وبعد تثبيت فلاسك، سنفتح ملف "app.py" ضمن المجلد "flask_app" لتحريره:
(env)user@localhost:$ nano app.py
سيعمل هذا الملف على إعداد الاتصال مع قاعدة البيانات وإنشاء وجهة فلاسك وحيدة قادرة على استخدام هذا الاتصال، لذا سنكتب ضمنه الشيفرة التالية:
import os import psycopg2 from flask import Flask, render_template app = Flask(__name__) def get_db_connection(): conn = psycopg2.connect(host='localhost', database='flask_db', user=os.environ['DB_USERNAME'], password=os.environ['DB_PASSWORD']) return conn @app.route('/') def index(): conn = get_db_connection() cur = conn.cursor() cur.execute('SELECT * FROM books;') books = cur.fetchall() cur.close() conn.close() return render_template('index.html', books=books)
نحفظ الملف ونغلقه.
استوردنا بدايةً في الشيفرة السابقة كل من الوحدة os
والمكتبة psycopg2
والصنف Flask
ودالة تصيير القوالب ()render_template
من حزمة فلاسك، كما أنشأنا نسخة من التطبيق باسم "app"، ثمّ عرفنا دالةً باسم ()get_db_connection
لإنشاء اتصال مع قاعدة البيانات "flask_db" باستخدام اسم المستخدم وكلمة المرور المخزنيْن في متغيرات البيئة DB_USERNAME
و DB_PASSWORD
على التوالي، ويعيد التابع كائن الاتصال conn
، الذي سنستخدمه للوصول إلى قاعدة البيانات.
استخدمنا بعد ذلك المُزخرف ()app.route
لإنشاء وجهة رئيسية /
ودالة عاملة في فلاسك باسم ()index
، وفتحنا اتصالًا مع قاعدة البيانات باستخدام الدالة get_db_connection()
، ثم أنشأنا مؤشرًا ونفذنا التعليمة ;SELECT * FROM books
من تعليمات SQL بغية الحصول على كل الكتب الموجودة في قاعدة البيانات، إذ أنّنا نستدعي التابع fetchall()
لحفظ البيانات ضمن متغير باسم books
، ومن ثم نغلق كل من المؤشر والاتصال مع قاعدة البيانات. ونهايةً نجعل القيمة المعادة استدعاءً للدالة ()render_template
لإخراج ملف قالب باسم "index.html" ممررين له قائمة الكتب التي جلبناها من قاعدة البيانات إلى المتغير books
.
الآن، وبغية عرض الكتب المُخزنة ضمن قاعدة البيانات في الصفحة الرئيسية للتطبيق، سننشئ بدايةً ملف قالب ليتضمّن كافة شيفرات HTML الأساسية اللازمة لتستخدمها لاحقًا القوالب الأُخرى، وهذا ما يجنبنا تكرار الشيفرات، ثمّ سننشئ ملف قالب الصفحة الرئيسية "index.html" المُصيّر أصلًا باستخدام الدالة ()index
.
سننشئ مجلدًا للقوالب باسم "templates" وننشئ ضمنه ملف قالب باسم "base.html"، والذي سيمثّل القالب الأساسي لبقية القوالب على النحو التالي:
(env)user@localhost:$ mkdir templates (env)user@localhost:$ nano templates/base.html
وسنكتب ضمنه الشيفرة التالية:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %}- FlaskApp</title> <style> nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } .book { padding: 20px; margin: 10px; background-color: #f7f4f4; } .review { margin-left: 50px; font-size: 20px; } </style> </head> <body> <nav> <a href="{{ url_for('index') }}">FlaskApp</a> <a href="#">About</a> </nav> <hr> <div class="content"> {% block content %} {% endblock %} </div> </body> </html>
احفظ الملف واغلقه.
يتضمّن القالب الأساسي كافّة الشيفرات المتداولة التي سنحتاجها في القوالب الأُخرى.
ستُستبدل لاحقًا كتلة العنوان title
بعنوان كل صفحة وكتلة المحتوى content
بمحتواها، أمّا عن شريط التصفح فسيتضمّن رابطين، الأوّل ينقل المُستخدم إلى الصفحة الرئيسية للتطبيق باستخدام الدالة المساعدة ()url_for
لتحقيق الربط مع دالة العرض ()index
، والآخر لصفحة المعلومات حول التطبيق About في حال قررت تضمينها في تطبيقك.
الآن، سنفتح ملف القالب باسم "index.html" وهو الاسم الذي حددناه في الملف "app.py":
(env)user@localhost:$ nano templates/index.html
ونضيف ضمنه الشيفرة التالية:
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Books {% endblock %}</h1> {% for book in books %} <div class='book'> <h3>#{{ book[0] }} - {{ book[1] }} BY {{ book[2] }}</h3> <i><p>({{ book[3] }} pages)</p></i> <p class='review'>{{ book[4] }}</p> <i><p>Added {{ book[5] }}</p></i> </div> {% endfor %} {% endblock %}
نحفظ الملف ونغلقه.
اعتمدنا في الشيفرة السابقة على الوراثة من ملف القالب "base.html" من خلال تعليمة extends
، واستبدلنا محتوى كتلة المحتوى content
مُستخدمين تنسيق العنوان من المستوى الأوّل <h1>
الذي يفي أيضًا بالغرض مثل عنوان للصفحة.
استخدمنا في السطر البرمجي {% for book in books %}
حلقة for
من تعليمات محرّك القوالب جينجا jinja، والهدف من استخدام هذه الحلقة هو المرور على كل عنصر في القائمة books
، إذ نظهر بدايةً رقم معرّف الكتاب ID والذي يمثّل العنصر الأوّل باستخدام الأمر [book[0
، ثمّ نظهر كلًا من عنوان الكتاب واسم مؤلفه وعدد صفحاته والنبذة التعريفية عنه وتاريخ إضافته.
الآن ومع وجودنا ضمن المجلد flask_app ومع كون البيئة الافتراضية مُفعّلة، نُعلم فلاسك بالتطبيق المراد تشغيله (وهو في حالتنا الملف "app.py") باستخدام متغير البيئة FLASK_APP
، ومن ثمّ نعيّن وضع التشغيل من خلال متغير البيئة FLASK_ENV
واخترنا هنا العمل في وضع التطوير development
، مع تشغيل مُنقّح الأخطاء. للمزيد من المعلومات حول مُنقّح الأخطاء في فلاسك ننصحك بقراءة المقال كيفية التعامل مع الأخطاء في تطبيقات فلاسك. لتنفيذ ما سبق سنشغّل الأوامر التالية:
(env)user@localhost:$ export FLASK_APP=app (env)user@localhost:$ export FLASK_ENV=development
وهنا لا بُدّ من التأكد من تعيين متغيرات البيئة DB_PASSWORD
و DB_USERNAME
في حال عدم إعدادها مُسبقًا:
(env)user@localhost:$ export DB_USERNAME="user" (env)user@localhost:$ export DB_PASSWORD="password"
والآن سنشغّل التطبيق باستخدام الأمر التالي:
(env)user@localhost:$ flask run
وبعد التأكد من كون خادم التطوير ما يزال قيد التشغيل، نذهب إلى الرابط التالي باستخدام المتصفح:
http://127.0.0.1:5000/
وعندها ستظهر لك الكتب التي أضفتها بدايةً إلى قاعدة البيانات بالشّكل التالي:
ومع نهاية هذه الخطوة نكون قد تمكنّا من عرض الكتب المُخزنة ضمن قاعدة البيانات مُسبقًا في الصفحة الرئيسية للتطبيق، أمّا في الخطوة التالية فسنعمل على إضافة وجهة جديدة من شأنها السماح للمُستخدمين بإضافة كتب جديدة إلى قاعدة البيانات.
الخطوة 5 - إضافة كتب جديدة
سنعمل في هذه الخطوة على إضافة وجهة جديدة للسماح للمُستخدمين بإضافة كتب جديدة ونبذات عنها إلى قاعدة البيانات. لذلك، سنضيف للتطبيق صفحة جديدة تتضمّن نموذج ويب لإدخال كل من عنوان الكتاب واسم مؤلفه وعدد صفحاته ونبذة عنه من قبل المستخدم.
سنفتح نافذة طرفية جديدة مع بقاء خادم التطوير قيد التشغيل، ثمّ سنفتح الملف "app.py":
(env)user@localhost:$ nano app.py
للتعامل مع نموذج الويب، لا بُد أولًا من استيراد التالي من حزمة فلاسك:
-
الكائن
request
العام المسؤول عن الوصول إلى بيانات الطلب المُرسَل من قبل المُستخدم. -
الدالة
url_for()
لتوليد عناوين الروابط. -
الدالة
redirect()
لإعادة توجيه المستخدم إلى صفحة التطبيق الرئيسية بعد إضافة كتاب إلى قاعدة البيانات.
أضِف هذه الاستيرادات إلى السطر الأوّل من الملف "app.py" كما يلي:
from flask import Flask, render_template, request, url_for, redirect # ...
ثمّ أضِف الوجهة التالية إلى نهايته:
# ... @app.route('/create/', methods=('GET', 'POST')) def create(): return render_template('create.html')
نحفظ الملف ونغلقه.
مرّرنا في هذه الوجهة صفًا tuple، يحتوي على القيم ('GET', 'POST')
إلى المعامل methods
بغية السماح بكلا نوعي طلبات HTTP وهما GET
و POST
، إذ تتخصّص الطلبات من النوع GET
بجلب البيانات من الخادم، أمّا الطلبات من النوع POST
فهي مُتخصّصة بإرسال البيانات إلى وجهة مُحدّدة، مع ملاحظة أنّ الطلبات من النوع GET
هي الوحيدة المسموحة افتراضيًا.
وحالما يطلب المستخدم الوجهة create/
باستخدام طلب من النوع GET
، سيُصيّر ملف قالب باسم "create.html"، وسنعدّل هذه الوجهة لاحقًا لتتعامل أيضًا مع الطلبات POST
اللازمة لدى ملء المُستخدمين للنماذج وإرسالها بغية إضافة كتب جديدة.
الآن، افتح ملف "create.html" الجديد داخل مجلد القوالب "templates" على النحو التالي:
(env)user@localhost:$ nano templates/create.html
واكتب ضمنه الشيفرة التالية:
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Add a New Book {% endblock %}</h1> <form method="post"> <p> <label for="title">Title</label> <input type="text" name="title" placeholder="Book title"> </input> </p> <p> <label for="author">Author</label> <input type="text" name="author" placeholder="Book author"> </input> </p> <p> <label for="pages_num">Number of pages</label> <input type="number" name="pages_num" placeholder="Number of pages"> </input> </p> <p> <label for="review">Review</label> <br> <textarea name="review" placeholder="Review" rows="15" cols="60" ></textarea> </p> <p> <button type="submit">Submit</button> </p> </form> {% endblock %}
احفظ الملف واغلقه.
اعتمدنا في الشيفرة السابقة على الوراثة من ملف القالب "base.html" من خلال تعليمة extends
، وعيّنا وسم ترويسة ليكون عنوانًا؛ واستخدمنا الوسم <form>
وضبطنا فيه السمة method
التي تحدّد نوع طلب HTTP لتكون من النوع POST
، ما يعني أنّ نموذج الويب هذا سيرسل طلبًا من النوع POST
؛ كما أضفنا صندوقًا نصيًا باسم title
لنستخدمه للوصول إلى بيانات عنوان الكتاب في الوجهة create/
؛ وأضفنا صندوقًا نصيًا مخصصًا لاسم مؤلف الكتاب، وصندوق عددي مخصص لعدد صفحات الكتاب؛ كما أضفنا حقلًا نصيًا مُتعدّد الأسطر مُخصّص للنبذة حول الكتاب، ونهايةً أضفنا إلى نهاية النموذج زر Submit لتأكيد إرساله.
الآن، وأثناء خادم التطوير، استخدم المتصفح للانتقال إلى الوجهة /create
:
http://127.0.0.1:5000/create
فستظهر لك صفحة إضافة كتاب جديد مع صندوق نصي لإدخال عنوان الكتاب، وآخر لإدخال اسم مؤلفه، ومربع لإدخال عدد صفحاته وحقل نصي مُتعدّد الأسطر لإدخال نبذة حول الكتاب، وزر لتأكيد إرسال النموذج، كما في الشّكل التالي:
يرسل نموذج الإدخال هذا طلبًا من النوع "POST" إلى الخادم، ولكن حتى هذه اللحظة لا يوجد شيفرة مسؤولة عن معالجة هذا الطلب في الوجهة create/
، وبالتالي لن يحدث شيء في حال ملء النموذج الآن وإرساله.
الآن، افتح الملف "app.py" لتعديله بحيث يتعامل مع الطلبات من النوع "POST" المُرسلة من قبل المستخدم:
(env)user@localhost:$ nano app.py
ونعدّل الوجهة create/
لتصبح كما يلي:
# ... @app.route('/create/', methods=('GET', 'POST')) def create(): if request.method == 'POST': title = request.form['title'] author = request.form['author'] pages_num = int(request.form['pages_num']) review = request.form['review'] conn = get_db_connection() cur = conn.cursor() cur.execute('INSERT INTO books (title, author, pages_num, review)' 'VALUES (%s, %s, %s, %s)', (title, author, pages_num, review)) conn.commit() cur.close() conn.close() return redirect(url_for('index')) return render_template('create.html')
احفظ الملف واغلقه.
تَمكَّنا باستخدام العبارة الشرطية if request.method == 'POST'
التي تقارن قيمة request.method
مع القيمة POST
من التحقُّق بأنّ التعليمات التالية لها لن تُنفّذ إلّا إذا كان الطلب الحالي هو فعلًا بطريقة POST
، ومن ثمّ قرأنا قيم كل من عنوان الكتاب واسم مؤلفه وعدد صفحاته والنبذة المرسلة عنه من الكائن request.form
الذي يمكِّننا من الوصول إلى بيانات نموذج الإدخال المُضمّنة في الطلب.
فتحنا بعد ذلك اتصالًا مع قاعدة البيانات باستخدام الدالة get_db_connection()
، وأنشأنا مؤشرًا، ونفّذنا الأمر INSERT INTO
من أوامر SQL باستخدام التابع ()execute
لإدخال كل من عنوان الكتاب واسم مؤلفه وعدد صفحاته ونبذة عنه إلى جدول الكتب books
في قاعدة البيانات وفق ما أُدخل أصلًا من قبل المُستخدم في النموذج.
نهايةً، حفظنا التغييرات باستخدام الدالة ()connection.commit
، وأُغلق الاتصال مع قاعدة البيانات باستخدام الدالة ()connection.close
، ثم أعدنا توجيه المًستخدم إلى الصفحة الرئيسية من التطبيق ليرى الكتاب الجديد المُضاف أسفل الكتب السابقة الموجودة أصلًا.
الآن وأثناء عمل خادم التطوير، استخدم المتصفح للانتقال إلى الوجهة /create
باستخدام الرابط:
http://127.0.0.1:5000/create
املأ النموذج بالبيانات التي تريد، وحال إرسال النموذج سيُعاد توجيهك إلى الصفحة الرئيسية للتطبيق لترى الكتاب الجديد فيها.
سنفتح الآن ملف القالب الأساسي base.html لإضافة رابط في شريط التصفّح للوصول إلى صفحة إضافة كتاب جديد:
(env)user@localhost:$ nano templates/base.html
ونعدّل الملف ليصبح كما يلي:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %} - FlaskApp</title> <style> nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } .book { padding: 20px; margin: 10px; background-color: #f7f4f4; } .review { margin-left: 50px; font-size: 20px; } </style> </head> <body> <nav> <a href="{{ url_for('index') }}">FlaskApp</a> <a href="{{ url_for('create') }}">Create</a> <a href="#">About</a> </nav> <hr> <div class="content"> {% block content %} {% endblock %} </div> </body> </html>
نحفظ الملف ونغلقه.
أضفنا في الشيفرة السابقة وسم رابط <a>
جديد إلى شريط التصفُّح والذي يشير إلى صفحة إضافة كتاب جديد، إذ سيظهر الرابط الجديد بتحديث الصفحة الرئيسية للتطبيق ضمن شريط التصفُّح.
ومع نهاية هذه الخطوة أصبح لدينا صفحة ذات نموذج إدخال لإضافة نبذات عن كتب جديدة، ولمزيدٍ من المعلومات حول النماذج وكيفية جعلها آمنة، ننصحك بقراءة المقال استخدام والتحقق من نماذج الويب ذات واجهة المستخدم التفاعلية في فلاسك باستخدام الإضافة Flask-WTF والمقال كيفية استخدام نماذج الويب في تطبيقات فلاسك.
الخاتمة
أنشأنا في هذا المقال تطبيق ويب مصغّر لتقييمات الكتب والذي يتخاطب مع قاعدة بيانات PostgreSQL، مع كامل الوظائف الأساسية من إضافة بيانات جديدة إلى قاعدة البيانات، وجلب بيانات منها لعرضها في صفحات التطبيق.
ترجمة -وبتصرف- للمقال How To Use a PostgreSQL Database in a Flask Application لصاحبه Abdelhadi Dyouri.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.