يُعد فلاسك إطار عمل لبناء تطبيقات ويب باستخدام لغة بايثون Python، ويمكن استخدام محرّك قواعد البيانات SQLite معه لتخزين بيانات التطبيق.
أمّا ماركداون Markdown فهي لغة لتوصيف تنسيق النصوص، شائعة الاستخدام لكتابة المحتوى بتنسيق سهل القراءة والتفسير من قِبل المتصفحات، إذ يمكن -باستخدام ماركداون- تنسيق النصوص الصرفة مع إضافة مميزات لها من عناوين وروابط وصور، ثمّ تحويلها إلى شيفرات HTML موافقة متضمّنةً نفس التنسيقات والمميزات.
بايثون-ماركداون Python-Markdown هي مكتبة في لغة بايثون تسمح بتحويل النصوص المُنسقّة باستخدام ماركداون إلى شيفرات HTML موافقة، والتي تتبع معاييّر ماركداون نفسها مع فروق بسيطة في شكل التعليمات.
سنستخدم في هذا المقال كل من إطار عمل فلاسك ومحرّك قواعد البيانات SQLite ومكتبة بايثون-ماركداون لبناء تطبيق ويب لتدوين الملاحظات، والذي يدعم تنسيق النصوص وفق ماركداون متيحًا للمستخدمين عرض وإنشاء وتنسيق الملاحظات لتتضمّن عناوينًا وروابطًا وقوائمًا وصور وغيرها من المميزات والتنسيقات، وسنستخدم حزمة بوتستراب Bootstrap لإضافة التنسيقات إلى مظهر التطبيق.
مستلزمات العمل
قبل المتابعة في هذا المقال لابدّ من:
- توفُّر بيئة برمجة بايثون 3 محلية، مثبّتة على حاسوبك، وسنفترض في مقالنا أن اسم مجلد المشروع هو "flask_notes".
- يجب فهم مختلف مفاهيم فلاسك الأساسية مثل إنشاء الوجهات وتصييّر قوالب HTML والاتصال بقاعدة بيانات SQLite.
الخطوة الأولى – إعداد مستلزمات إنشاء التطبيق
سنفعّل في هذه الخطوة بيئة بايثون، وسنثبّت كل من فلاسك ومكتبة بايثون-ماركداون باستخدام مثبّت الحزمة "pip"، ومن ثمّ سننشئ قاعدة البيانات التي سنستخدمها لتخزين الملاحظات وبعض البيانات النموذجية.
سنفعّل بدايةً البيئة البرمجية في حال لم تكن مُفعلّة أصلًا، كما يلي:
$ source env/bin/activate
الآن وبعد تفعيل البيئة البرمجية، سنثبّت كل من فلاسك ومكتبة بايثون-ماركداون باستخدام الأمر التالي:
(env)user@localhost:$ pip install flask markdown
سننشئ الآن ملف تخطيط قاعدة البيانات باسم "schema.sql"، والذي سيتضمّن أوامر SQL اللازمة لإنشاء جدول الملاحظات "notes"، سننشئ هذا الملف ضمن مجلد المشروع "flask_notes":
(env)user@localhost:$ nano schema.sql
وسنكتب أوامر SQL التالية ضمن الملف الذي أنشأناه:
DROP TABLE IF EXISTS notes; CREATE TABLE notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, content TEXT NOT NULL );
يعمل أوّل أمر من أوامر SQL وهو:
;DROP TABLE IF EXISTS posts
على حذف أي جداول موجودة مسبقًا باسم notes
وهذا ما يجنبنا أي تضاربات قد تحصل مُستقبلًا نتيجة وجود جدولين بنفس الاسم وبأعمدة مُختلفة في قاعدة البيانات، والذي قد يؤدي لتوقف تنفيذ الشيفرة نتيجة عدم توافق الجدول الموجود مع تخطيط قاعدة البيانات، ومن الجدير بالملاحظة أنّ هذا الأمر سيحذف كل المحتويات في قاعدة البيانات في كل مرة تشغّل فيها أوامر SQL هذه، لذا لا تُدخل أي بيانات مهمّة في التطبيق حتى تنتهي من كافّة الخطوات التعليميّة في المقال وتجربة نتائجه النهائية؛ أمّا الأمر الثاني من أوامر SQL وهو
CREATE TABLE notes
فيعمل على إنشاء جدول باسم notes
بالأعمدة التالية:
-
id
: وهو رقم صحيح موجب يمثّل المفتاح الأساسي للجدول، إذ سيُخصص رقم فريد لكل من مدخلات الجدول، والمدخلات في حالتنا هي الملاحظات. -
created
: ويمثّل تاريخ إنشاء الملاحظة والذي يُملأ تلقائيًا بتاريخ ووقت إضافة الملاحظة إلى قاعدة البيانات. -
content
: محتوى الملاحظة.
احفظ الملف واغلقه.
الآن سننشئ قاعدة البيانات باستخدام ملف تخطيط قاعدة البيانات "schema.sql"، لذا سنفتح ملف باسم "init_db.py" ضمن المجلد "flask_notes":
(env)user@localhost:$ nano init_db.py
وسنكتب ضمنه الشيفرة التالية:
import sqlite3 connection = sqlite3.connect('database.db') with open('schema.sql') as f: connection.executescript(f.read()) cur = connection.cursor() cur.execute("INSERT INTO notes (content) VALUES (?)", ('# The First Note',)) cur.execute("INSERT INTO notes (content) VALUES (?)", ('_Another note_',)) cur.execute("INSERT INTO notes (content) VALUES (?)", ('Visit [this page](https://www.digitalocean.com/community/tutorials) for more tutorials.',)) connection.commit() connection.close()
استوردنا في الشيفرة السابقة بدايةً وحدة sqlite3
، ثمّ اتصلنا بملف اسمه "database.db"، والذي يُنشَأ تلقائيًا عند تنفيذ البرنامج، والمُمثّل لقاعدة البيانات التي ستتضمّن كافّة بيانات التطبيق، ثمّ فتحنا ملف تخطيط قاعدة البيانات "schema.sql" وشغّلناه باستخدام الدالة ()executescript
المسؤولة عن تنفيذ عدّة تعليمات SQL معًا، وبالنتيجة أنشأنا جدول الملاحظات notes
.
ثمّ نُفّذت عدّة أوامر إدخال INSERT
من أوامر SQL باستخدام كائن المؤشّر Cursor object بهدف إنشاء ثلاث ملاحظات. استخدمنا تعليمات ماركداون بحيث تكون الملاحظة الأولى بتنسيق عنوان من المستوى الأوّل <h1>
، والملاحظة الثانية بخط مائل، والملاحظة الثالثة تتضمّن رابطًا، كما استخدمنا الموضع المؤقّت ?
في الدالة ()execute
ومرّرنا إليها السجل الحاوي على محتويات الملاحظة بما يضمن إدخال البيانات إلى القاعدة بأمان.
نهايةً حفظنا التغييرات وأغلقنا الاتصال مع قاعدة البيانات.
احفظ الملف واغلقه.
الآن نشغّل البرنامج كما يلي:
(env)user@localhost:$ python init_db.py
سيظهر بعد تنفيذ البرنامج ملفٌ جديد باسم "database.db" ضمن المجلّد "flask_notes"، ومع نهاية هذه الخطوة نكون قد فعّلنا البيئة البرمجية، وثبّتنا كلًا من فلاسك ومكتبة بايثون-ماركداون، كما أنشأنا قاعدة بيانات SQLite، وسنجلب في الخطوة التالية الملاحظات من قاعدة البيانات ونحوّلها إلى ملف من نوع HTML لعرضها ضمن الصفحة الرئيسية للتطبيق.
الخطوة الثانية – عرض الملاحظات في الصفحة الرئيسية للتطبيق
سننشئ في هذه الخطوة تطبيق فلاسك يتصل بقاعدة البيانات ليعرض البيانات المُخزّنة فيها، إذ سنحوّل النص المُنسّق باستخدام ماركداون إلى ملف HTML والذي سيُصيّر ضمن الصفحة الرئيسية للتطبيق.
بدايةً، سننشئ ملف التطبيق "app.py" ضمن المجلد "flask_notes" على النحو التالي:
(env)user@localhost:$ nano app.py
وسنكتب ضمنه الشيفرة التالية:
import sqlite3 import markdown from flask import Flask, render_template, request, flash, redirect, url_for def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn
استوردنا في بداية الشيفرة السابقة كلًا من وحدة sqlite3
وحزمة markdown
ومُساعد فلاسك، إذ اتصلنا مع ملف قاعدة البيانات "database.db" بواسطة الدالة ()get_db_connection
، ثمّ أسندنا القيمة sqlite3.Row
للسمة row_factory
، وهذا ما سيمكنّنا من الوصول إلى الجداول بأسمائها، وبالنتيجة سيعيد الاتصال مع قاعدة البيانات سجلات تسلك سلوك قواميس بايثون اعتيادية. نهايةً، ستعيد الدالة كائن الاتصال conn
الذي سنستخدمه في الوصول إلى قاعدة البيانات.
سنضيف بعد الدالة السابقة جزء الشيفرة التالي:
#. . . app = Flask(__name__) app.config['SECRET_KEY'] = 'this should be a secret random string'
وبذلك نكون أنشأنا كائن تطبيق فلاسك وخصّصنا له مفتاح أمان، ما يضمن كون جلسات العمل آمنة.
الآن سنضيف الشيفرة التالية:
#. . . @app.route('/') def index(): conn = get_db_connection() db_notes = conn.execute('SELECT id, created, content FROM notes;').fetchall() conn.close() notes = [] for note in db_notes: note = dict(note) note['content'] = markdown.markdown(note['content']) notes.append(note) return render_template('index.html', notes=notes)
دالة ()index
العاملة في فلاسك هي دالةٌ مُصمّمة باستخدام المُزخرف app.route@
، ويحوّل فلاسك القيمة المعادة من هذه الدالة إلى استجابة من نوع HTTP لتُعرض ضمن عميل HTTP مثل متصفح الويب.
ونفتح ضمن هذه الدالة اتصالًا مع قاعدة البيانات وننفّذ تعليمة الاختيار SELECT
من تعليمات SQL المسؤولة عن جلب المعرّف ID، وتاريخ الإنشاء والمحتوى لكل سجل (ملاحظة) من محتويات جدول الملاحظات notes
، ونستخدم الدالة ()fetchall
لجلب قائمة بجميع السجلات وحفظها ضمن المتغير db_notes
، ونهايةً نغلق الاتصال.
الآن، لتحويل محتوى الملاحظات من تنسيق ماركداون إلى HTML، سننشئ قائمةً جديدةً فارغةً باسم notes
، لنمر على كافّة عناصر القائمة المحفوظة في المتغير db_notes
ونحوّل كل ملاحظة فيها من النمط sqlite3.Row
إلى قاموس بايثون عادي باستخدام دالة بايثون ()dict
لتمكين عملية التحويل، ثمّ سنستخدم الدالة ()markdown.markdown
لتغيير قيمة محتوى الملاحظة ['note['content
إلى HTML، إذ سيعيد مثلًا استدعاء الدالة ('markdown.markdown('#Hi
سيعيد السلسلة النصية '<h1>Hi</h1>'
وذلك لأنّ الرمز #
في ماركداون يدل على عنوان من المستوى الأوّل <h1>
. الآن، وبعد تعديل محتوى ['note['content
نضيف الملاحظة إلى القائمة الجديدة المُسمّاة notes
، ونهايةً نصيّر قالب HTML باسم "index.html" عن طريق تمرير القائمة notes
إليه.
وبعد هذه التعديلات ستبدو الشيفرة على النحو التالي:
import sqlite3 import markdown from flask import Flask, render_template, request, flash, redirect, url_for def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn app = Flask(__name__) app.config['SECRET_KEY'] = 'this should be a secret random string' @app.route('/') def index(): conn = get_db_connection() db_notes = conn.execute('SELECT id, created, content FROM notes;').fetchall() conn.close() notes = [] for note in db_notes: note = dict(note) note['content'] = markdown.markdown(note['content']) notes.append(note) return render_template('index.html', notes=notes)
احفظ الملف واغلقه.
الآن سننشئ القالب الرئيسي وملف القالب index.html، لذا سنضيف مجلدًا للقوالب باسم "templates" ضمن المجلد "flask_notes"، وفيه سنفتح ملف باسم "base.html" كما يلي:
(env)user@localhost:$ mkdir templates (env)user@localhost:$ nano templates/base.html
ونضيف الشيفرة التالية ضمن الملف "base.html"، مع ملاحظة أنّنا نستخدم بوتستراب هنا.
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <title>{% block title %} {% endblock %}</title> </head> <body> <nav class="navbar navbar-expand-md navbar-light bg-light"> <a class="navbar-brand" href="{{ url_for('index')}}">FlaskNotes</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="#">About</a> </li> </ul> </div> </nav> <div class="container"> {% for message in get_flashed_messages() %} <div class="alert alert-danger">{{ message }}</div> {% endfor %} {% block content %} {% endblock %} </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </body> </html>
الجزء الأكبر من الشيفرة السابقة هو تعليمات HTML معيارية وشيفرات لازمة لعمل بوتستراب، إذ تزوّد الوسوم <meta>
متصفح الويب بالمعلومات، في حين ينشئ الوسم <link>
ارتباطًا إلى ملفات CSS الخاصة ببوتستراب، ويضمِّن الوسم <script>
شيفرة جافا سكربت مسؤولة عن بعض ميزات بوتستراب الإضافية.
يسمح الوسم <title>{% block title %} {% endblock %}</title>
للقوالب الوارثة (التي ترث خصائصها من قالب HTML الرئيسي) بتعريف عناوين خاصّة بها. استخدمنا الحلقة التكرارية ()for message in get_flashed_messages
لعرض الرسائل الخاطفة التحذيرية والتنبيهية؛ أما محتوى القوالب الوارثة فيُضمّن في الموضع المؤقت {% block content %} {% endblock %}
الذي يوفرّه القالب الرئيسي مثل إضافة خاصّة بكل قالب يرث منه ما يجنّب تكرار التعليمات.
احفظ الملف واغلقه.
الآن سننشئ الملف "index.html" الذي سيرث قالب HTML الرئيسي "base.html":
(env)user@localhost:$ nano templates/index.html
وضمنه سنكتب الشيفرة التالية:
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Welcome to FlaskNotes {% endblock %}</h1> {% for note in notes %} <div class="card"> <div class="card-body"> <span class="badge badge-primary">#{{ note['id'] }}</span> <span class="badge badge-default">{{ note['created'] }}</span> <hr> <p>{{ note['content'] |safe }}</p> </div> </div> <hr> {% endfor %} {% endblock %}
وبذلك نكون قد ورثنا خصائص القالب "base.html"، ثمّ عرّفنا عنوان title الملف، ومررنا باستخدام حلقة "for" التكرارية على الملاحظات لنعرض كل منها ضمن بطاقة بوتستراب Bootstrap card، بما يتضمّن معرّف وتاريخ إنشاء ومحتوى الملاحظة، والتي قد حوّلناها أصلًا إلى تنسيق HTML ضمن دالة فلاسك ()index
.
وقد طبقنا المُرشّح safe|
من محرّك القوالب جينجا jinja على محتوى الملاحظات باستخدام |
وهذا يُشبه مبدأ استدعاء دالة ما في لغة بايثون (أي أنّ الأمر safe|
من أوامر جينجا يشابه بناء الأمر safe(note['content'])
في لغة بايثون)، ويُمكّن المُرشّح المتصفح من تصييّر شيفرة HTML، وبدونه ستظهر تعليمات HTML ضمن الصفحة كما هي أي نص صرف، وتُعد هذه ميزة أمان تسمّى "تهريب شيفرة" HTML escaping، من شأنها منع المتصفح من تفسير شيفرات HTML الخبيثة المُسببة لثغرة أمنية خطيرة تسمّى حقن الشيفرات البرمجية cross-site scripting -أو اختصارًا XSS-، وبما أنّ مكتبة بايثون-مارك داون تعيد بالضرورة شيفرات HTML آمنة، فبإمكانك استخدام المُرشّح safe|
معها ليتمكّن المتصفح من تصييّر هذه الشيفرات، ومن المهم عدم استخدام هذا المُرشّح إلّا مع شيفرات HTML الآمنة وموثوقة المصدر مثل تلك الناتجة عن مكتبة بايثون-ماركداون.
احفظ الملف واغلقه.
الآن سنسند القيم اللازمة لمتغيرات بيئة فلاسك، ثمّ سنشغّل التطبيق باستخدام الأوامر التالية:
(env)user@localhost:$ export FLASK_APP=app (env)user@localhost:$ export FLASK_ENV=development (env)user@localhost:$ flask run
يحدّد متغير فلاسك FLASK_APP
في الشيفرة السابقة التطبيق المراد تشغيله وهو في حالتنا الملف "app.py"، في حين يحدّد المتغير FLASK_ENV
وضع التشغيل وهنا قد اخترنا وضع التطوير development
، الذي يعني أنّ التطبيق سيعمل في وضع التطوير مع تشغيل مُنقّح الأخطاء (ومن المهم عدم اختيار هذا الوضع في مرحلة الاستخدام الفعلي للتطبيق أي مرحلة نشر المُنتج)، ونهايةً شغلنا التطبيق باستخدام الأمر flask run
.
الآن وبالذهاب إلى الرابط "/http://127.0.0.1:5000" في المتصفّح سنحصل على الشكل التالي:
وبذلك نجد أنّ المتصفح قد صيّر كافّة الملاحظات على أنّها مُصاغة في HTML ولم تظهر نصًا عاديًا كما هو.
ومع نهاية هذه الخطوة نكون قد أنشأنا تطبيق فلاسك قادر على الاتصال مع قاعدة البيانات لجلب الملاحظات المحفوظة وتحويل محتوياتها من نص مُنسّق باستخدام ماركداون إلى ما يقابلها في HTML، ليجري تصيير شيفرات HTML هذه في الصفحة الرئيسية للتطبيق.
سنضيف في الخطوة التالية اتجاه route يُمكّن المستخدمين من إضافة ملاحظات جديدة والذي بإمكانهم كتابته بتنسيق مارك داون.
الخطوة الثالثة – إضافة ملاحظات جديدة
سنضيف في هذه الخطوة اتجاه جديد يمكّن المستخدمين من إضافة ملاحظات جديدة مُنسقّة باستخدام مارك داون، إذ سيحفظ التطبيق هذه الملاحظات في قاعدة البيانات لعرضها لاحقًا في صفحة التطبيق الرئيسية مُنسقّة بصورةٍ صحيحة وفقًا للتنسيقات التي أضافها المستخدم، كما سنضيف زرًا في شريط التصفح لنقل المستخدم مباشرةً إلى صفحة إضافة ملاحظة جديدة.
سنستخدم نموذج ويب ليُدخل المستخدم ضمنه الملاحظة الجديدة لإضافتها إلى التطبيق وحفظها في قاعدة البيانات.
لذا سنفتح الملف "app.py" بهدف إضافة الاتجاه الجديد:
(env)user@localhost:$ nano app.py
وضمنه سنضيف الشيفرة التالية:
#. . . @app.route('/create/', methods=('GET', 'POST')) def create(): conn = get_db_connection() if request.method == 'POST': content = request.form['content'] if not content: flash('Content is required!') return redirect(url_for('index')) conn.execute('INSERT INTO notes (content) VALUES (?)', (content,)) conn.commit() conn.close() return redirect(url_for('index')) return render_template('create.html')
وطالما أنّنا سنستخدم هذا الاتجاه لإدخال بيانات جديدة إلى قاعدة البيانات عبر نموذج ويب فكان لا بُدّ من تمكين كلا طريقتي الطلبات GET و POST باستخدام التعليمة ('methods=('GET', 'POST
في المُزخرف ()app.route
، ثمّ فتحنا اتصالًا مع قاعدة البيانات باستخدام دالة فلاسك ()create
.
في الشيفرة السابقة: عندما يُدخل المستخدم بيانات في النموذج ويرسلها سيتحقق الشرط 'request.method == 'POST
لأنّ هذا الطلب من النوع POST، وعندها نستخلص محتوى الملاحظة باستخدام ['request.form['content
لنحفظه ضمن المتغير content
، فإذا كان حقل المحتوى فارغًا نعرض للمستخدم رسالةً تحذيريةً تفيد بأن حقل المحتوى مطلوب "'!Content is required" ونعيد توجيهه إلى صفحة التطبيق الرئيسية، وإلّا وفي حال كون حقل المحتوى في النموذج ليس فارغًا فنستخدم تعليمة الإدخال INSERT
في SQL لإضافة محتوى الملاحظة إلى جدول الملاحظات notes
في قاعدة البيانات، ثمّ نحفظ التغييرات ونغلق الاتصال مع قاعدة البيانات ونعيد توجيه المستخدم إلى الصفحة الرئيسية.
إذا كان الطلب من النوع GET، فهذا يعني أنّ المستخدم قد زار الصفحة فقط، وعندها نصيّر قالب HTML ذا الاسم "create.html".
احفظ الملف واغلقه.
نفتح الآن ملف القالب "create.html":
(env)user@localhost:$ nano templates/create.html
ونضيف ضمنه الشيفرة التالية:
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Add a New Note {% endblock %}</h1> <form method="post"> <div class="form-group"> <label for="content">Note Content</label> <textarea type="text" name="content" placeholder="Note content, you can use Markdown syntax" class="form-control" value="{{ request.form['content'] }}" autofocus></textarea> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Submit</button> </div> </form> {% endblock %}
استخدمنا في الشيفرة المضافة إلى القالب نموذجًا للإدخال ذا حقلٍ نصي يتيح للمستخدم كتابة محتوى الملاحظة، وللحفاظ على إمكانية الوصول إلى البيانات المدخلة في حال حدوث أي أخطاء (مثل حالة ترك حقل محتوى الملاحظة فارغًا)، نستخدم الكائن request.form
لحفظ البيانات المدخلة في النموذج، كما أضفنا زرًا لتأكيد النموذج أسفل الحقل النصي بحيث يتيح للمستخدم تأكيد النموذج وإرساله ضمن طلب بطريقة POST.
احفظ الملف واغلقه.
الآن سنفتح ملف قالب HTML الرئيسي "base.html" لإضافة زر "ملاحظة جديدة "New Note" إلى شريط التصفّح:
(env)user@localhost:$ nano templates/base.html
ونعدّل الشيفرة في الملف لتصبح كما يلي:
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <title>{% block title %} {% endblock %}</title> </head> <body> <nav class="navbar navbar-expand-md navbar-light bg-light"> <a class="navbar-brand" href="{{ url_for('index')}}">FlaskNotes</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="#">About</a> </li> <li class="nav-item active"> <a class="nav-link" href="{{ url_for('create') }}">New Note</a> </li> </ul> </div> </nav> <div class="container"> {% for message in get_flashed_messages() %} <div class="alert alert-danger">{{ message }}</div> {% endfor %} {% block content %} {% endblock %} </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </body> </html>
أضفنا في الشيفرة السابقة عنصر قائمة <li>
إلى شريط التصفح واستخدمنا فيه الدالة ()url_for
لتأمين الربط مع دالة فلاسك ()create
، وهذا ما يضمن الوصول إلى صفحة إنشاء ملاحظة جديدة من شريط التصفح.
الآن شغّل خادم التطوير إن لم يكن مُشغّلًا أصلًا باستخدام الأمر التالي:
(env)user@localhost:$ flask run
الآن، استخدم الرابط "http://127.0.0.1:5000/create" في المتصفّح وأضف الملاحظة التالية المُنسقّة باستخدام مارك داون:
### Flask Flask is a **web** framework for _Python_. Here is the Flask logo: ![Flask Logo](https://flask.palletsprojects.com/en/1.1.x/_images/flask-logo.png)
يتضمّن تنسيق مارك داون للملاحظة السابقة عنوانًا من المستوى الثالث h3
ويجعل كلمة web
بخط غامق وكلمة python
بخط مائل، كما يتضمّن صورة.
وبتأكيد الملاحظة وإرسالها سيحوّل التطبيق التنسيقات إلى مقابلاتها في HTML لتظهر بالشكل التالي:
الخاتمة
أنشأنا في هذا المقال تطبيق في فلاسك لإدخال الملاحظات المُنسّقة باستخدام ماركداون، ما يسمح للمستخدمين بإضافة التنسيقات لملاحظاتهم بما يتضمّن العناوين باختلاف مستوياتها، والخطوط الغامقة والمائلة، وإضافة الصور والروابط.
وربطنا التطبيق مع قاعدة بيانات SQLite لحفظ البيانات وجلبها، إذ سيفسّر التطبيق النصوص المُنسقّة باستخدام ماركداون إلى مقابلاتها من شيفرات HTML ليتمكّن المتصفح من تصييّرها بشكلها المطلوب.
ترجمة -وبتصرف- للمقال How To Use Python-Markdown with Flask and SQLite لصاحبه Abdelhadi Dyouri.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.