اذهب إلى المحتوى

استخدام مكتبة بايثون-ماركداون مع إطار عمل فلاسك ومحرك قواعد البيانات SQLite


محمد الخضور

يُعد فلاسك إطار عمل لبناء تطبيقات ويب باستخدام لغة بايثون 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" في المتصفّح سنحصل على الشكل التالي:

first_step2a.png

وبذلك نجد أنّ المتصفح قد صيّر كافّة الملاحظات على أنّها مُصاغة في 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 لتظهر بالشكل التالي:

second_step3a.png

الخاتمة

أنشأنا في هذا المقال تطبيق في فلاسك لإدخال الملاحظات المُنسّقة باستخدام ماركداون، ما يسمح للمستخدمين بإضافة التنسيقات لملاحظاتهم بما يتضمّن العناوين باختلاف مستوياتها، والخطوط الغامقة والمائلة، وإضافة الصور والروابط.

وربطنا التطبيق مع قاعدة بيانات SQLite لحفظ البيانات وجلبها، إذ سيفسّر التطبيق النصوص المُنسقّة باستخدام ماركداون إلى مقابلاتها من شيفرات HTML ليتمكّن المتصفح من تصييّرها بشكلها المطلوب.

ترجمة -وبتصرف- للمقال How To Use Python-Markdown with Flask and SQLite لصاحبه Abdelhadi Dyouri.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...