flask 101 اساسيات إطار العمل Flask: القوالب


عبدالهادي الديوري

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

flask-jinja-templates.png

استعمال قوالب HTML

يُمكن فصل ملفّات HTML عن ملفّ لغة بايثون بوضعها داخل مُجلّد باسم templates (اسم المُجلّد مهم)، ويكون هذا المُجلّد في نفس مسار الملف app.py. بعد ذلك يُمكن تقديم الملفّ من قبل التّطبيق المكتوب بلغة بايثون عبر الدّالة render_template (يجب استيرادها في البداية) مع تمرير اسم الملفّ.

أولا ادخل إلى مُجلّد flask_app ثمّ أنشئ مجلّدا جديدا باسم templates بعدها أنشئ ملفّا باسم index.html داخل هذا المُجلّد، وضع به شيفرة HTML التّاليّة:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>الصّفحة الرّئيسيّة</title>
</head>
<body style="direction: rtl;">
    <h1>السّلام عليكم ورحمة الله</h1>
</body> 
</html>

يُمكننا الآن أن نجعل التّطبيق app.py يُقدم هذه الشّيفرة، وذلك أولا باستيراد الدالة render_template واستدعائها مُباشرة بعد جُملة return.

# -*- coding:utf8 -*-
from flask import Flask, render_template
app = Flask(__name__)

# Home Page
@app.route("/")
def home():
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)

بعد تشغيل التّطبيق زر العنوان http://127.0.0.1:5000.

ستُلاحظ بأنّ مُحتوى الصّفحة هو نفسه مُحتوى الملفّ index.html، ويُمكنك مُشاهدة مصدر الصّفحة بالضّغط على زر الفأرة الأيمن واختيار الخيار View Page Source.

تمرير متغير وعرض قيمته

يُمكن تمرير المُتغيّرات إلى ملفّ HTML لعرض قيمها بإضافتها كمُعاملات للدّالة render_template.

# Home Page
@app.route("/")
def home():
    return render_template('index.html', page = u"الصّفحة الرّئيسيّة")

لاحظ بأنّنا قُمنا بإضافة المُتغيّر page وأسندنا له القيمة "الصّفحة الرّئيسيّة"، يُمكننا الآن عرضه في ملفّ HTML بإحاطته بعلامات {{ }}، في المثال التّالي، نعرض قيمة المُتغيّر page داخل وسم h3 مُباشرة بعد الجملة الرّئيسيّة.

<body style="direction: rtl;">
    <h1>السّلام عليكم ورحمة الله</h1>
    <h3>{{ page }}</h3>
</body>

بعد تعديل الملفّ وحفظه ستُلاحظ بأنّ السّطر {{ page }} قد تغيّر إلى عبارة "الصّفحة الرّئيسيّة". 

عند مُشاهدة مصدر الصّفحة ستُلاحظ بأنّ السّطر:

<h3>{{ page }}</h3>

قد تحوّل إلى السّطر:

<h3>الصّفحة الرّئيسيّة</h3>

وهذا هو العمل الرّئيسي لمُحرّك القوالب Jinja2.

يُمكن كذلك تقديم نفس ملفّ HTML عبر أكثر من توجيه، كما يُمكن أن تكون قيم المُتغيّرات المُمرّرة مُختلفة، فمثلا المُوّجه الرّئيسي / سيؤدي إلى تقديم الصّفحة index.html مع مُتغيّر يحمل القيمة "الصّفحة الرّئيسيّة". ويُمكن إضافة توجيه آخر /hello، مع تقديم نفس الملفّ ومُتغيّر page بالقيمة "صفحة التّرحيب". 

وبالتالي سيُصبح التطبيق الكامل كالآتي:

# -*- coding:utf8 -*-
from flask import Flask, render_template
app = Flask(__name__)

# Home Page
@app.route("/")
def home():
    return render_template('index.html', page = u"الصّفحة الرّئيسيّة")

# Hello Page
@app.route("/hello")
def hello():
    return render_template('index.html', page = u"صفحة التّرحيب")


if __name__ == "__main__":
    app.run(debug=True)

يُمكنك كذلك عرض قيمة المُتغيّر في أكثر من موضع، مثلا يُمكنك وضعه كعنوان للصّفحة داخل الوسم title.

<head>
    <meta charset="UTF-8">
    <title>{{ page }}</title>
</head>

بعد تشغيل التّطبيق، سنتمكّن من الوصول إلى صفحتين تُقدّمان نفس ملفّ HTML مع اختلاف في قيمة المُتغيّر page (أي اختلاف في مُحتوى الوسم h3 وعنوان مُناسب لكلّ صفحة). 

الصّفحة الرّئيسيّة: http://127.0.0.1:5000
صفحة التّرحيب: http://127.0.0.1:5000/hello

التعليقات

يُمكن وضع تعليقات في مُحرّك القوالب Jinja2 بإحاطتها بعلامات {# تعليق #}، والتّعليق لن يظهر حتى بعد النّظر إلى مصدر الصّفحة، ويُمكن استعماله كالتّالي:

<body style="direction: rtl;">
    <h1>السّلام عليكم ورحمة الله</h1>
   {# هذا تعليق لن يظهر للزائر #}
    <h3>{{ page }}</h3>
</body>

الجمل الشرطية في محرك القوالب Jinja2

تعرّفنا إلى الآن على طريقة تقديم ملفّات HTML وكيفيّة تمرير المُتغيّرات من ملفّ بايثون إلى ملفّ HTML وكيفيّة عرض قيّمها. 

لكن مُحرّك القوالب Jinja2 ليس لهذا الغرض فقط، بل يُمكّننا كذلك من استعمال خصائص لغة بايثون، مثل الجمل الشّرطية وحلقة التّكرار for وغير ذلك.

انتبه فقط إلى حقيقة أنّ بنية الجمل Syntax الخاصة بلغة بايثون مُختلفة عن بنية الجملة في مُحرّك القوالب Jinja2. فمثلا جملة شرطيّة في لغة بايثون ستكون كالتّالي:

x = 10
if x == 10:
    print x

أمّا في Jinja2 فستكون كالتّالي:

{% set x = 10 %}
{% if x == 10 %}
    {{ x }}
{% endif %}

أول فرق قد تُلاحظه هو أنّ جميع الشيفرات مُحاطة بالعلامات {% %} وعرض المُغيّر يكون داخل علامات {{}}. وبالنّسبة لتعريف مُتغيّر وإسناد قيمة له فيلزمه كلمة set. كما يجب إنهاء الجملة الشّرطية بجملة endif، كما أنّ الإزاحة ليست ضروريّة في مُحرّك Jinja2. 

مُلاحظة: يُفضّل عدم تعريف المُتغيّرات مُباشرة في ملفّات HTML إلا لحاجة، ومن الأفضل تعريفها داخل ملفّات بايثون.

لإضافة جملتي elif و else يُمكن القيام بالتّالي:

{% set x = 6 %}
{% if x == 10 %}
    x يُساوي 10
{% elif x == 5 %}
    x يُساوي 5
{% else %}
    x يُساوي شيئا آخر
{% endif %}

جرّب تغيير قيمة المُتغيّر x وانظر إلى النّتيجة.

حلقة for

تعرّفنا على كيفيّة عرض مُتغيّر يحمل قيمة واحدة فقط، لكن ماذا لو أردنا أن نعرض عناصر قائمة ما، بحيث يعرض كلّ عنصر داخل وسم مُعيّن، يُمكن ذلك عبر حلقة for ويُمكن كتابتها كالآتي:

{% for item in list %}
    <h1> {{ item }} </h1>
{% endfor %}

بحيث list هي القائمة مُتعدّدة العناصر.

مثال

لنفترض بأنّه لدينا قائمة مقالات لعرضها للزائر، بحيث تكون القائمة كالتّالي:

posts = [ u"مُحتوى المقال الأول", u"مُحتوى المقال الثاني", u"مُحتوى المقال الثالث", u"مُحتوى المقال الرابع" ]

سنقوم أولا بإنشاء توجيه جديد باسم posts إلى ملفّ app.py وسنُمرّر ملفّ index.html مع تمرير القائمة إلى الملف.

# Posts Page
@app.route("/posts")
def posts():
    posts = [
    u"مُحتوى المقال الأول",
    u"مُحتوى المقال الثاني",
    u"مُحتوى المقال الثالث",
    u"مُحتوى المقال الرابع"
    ]
    return render_template('index.html', posts = posts, page = u"صفحة عرض المقالات")

لاحظ بأنّ المُعامل الثاني للدّالة render_template هو posts = posts، الأمر يعني بأنّ القائمة المُمرّرة للقالب اسمها posts وستحمل قيّم القائمة posts المتواجدة في الأعلى.

يُمكن عرض كل مقال داخل وسم p بالطّريقة التّاليّة:

{% for post in posts %}
	<p> {{ post }} </p>
{% endfor %}

يُمكنك الآن زيارة مُدوّنتك المُتواضعة عبر الرّابط http://127.0.0.1:5000/posts.

إذا قُمت بعرض مصدر الصّفحة فستُلاحظ ما يلي:

<h3>صفحة عرض المقالات</h3>
    <p> مُحتوى المقال الأول </p>
    <p> مُحتوى المقال الثاني </p>
    <p> مُحتوى المقال الثالث </p>
    <p> مُحتوى المقال الرابع </p>

لاحظ بأنّ الوسم p قد تكرّر مع عرض كلّ عنصر.

تنسيق الصفحات، إضافة الملفات الساكنة

الملفّات السّاكنة هي الصّور وملفّات CSS أو Javascript وتوضع في مُجلّد باسم static بجانب المُجلّد templates

في هذا القسم سنُضيف ملفّ CSS لتنسيق عرض المقالات أعلاه. 

أنشئ مُجلّدا باسم static داخل مُجلّد المشروع (بجانب المُجلّد templates). 

بعدها أنشئ 3 مُجلّدات داخل هذا المُجلّد أسماؤها كالتّالي:

static
---| css    # هنا توضع ملفّات التّنسيق
---| js     # في هذا المُجلّد يُمكنك وضع ملفّات جافاسكريبت
---| img    # ضع الصّور في هذا المُجلّد

بعد إنشاء المُجلّدات أنشئ ملفّ تنسيق باسم style.css داخل مُجلّد css، وضع به ما يلي:

body {
    text-align: center;
}

h3 {
    color:#1383EA;
}

p {
    font-size: 14px;
}

للرّبط بين ملفّ css وملفّ HTML، يُمكننا الاعتماد على دالة url_for التّي يُقدّمها مُحرّك القوالب Jinja2 لإعطاء مرونة أكثر في التّعامل مع ملفّات مُتعدّدة، واستخدامها يكون كالتّالي:

{{ url_for('static', filename='path/to/file') }}

مع تغيير path/to/file إلى مسار الملفّ. 

وبالتّالي لتضمين ملفّ style.css داخل ملفّ index.html فسيتوجّب علينا إضافة السّطر التّالي إلى وسم head.

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css')}}">

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

مُلاحظة: عند التّعديل على ملفّ التنسيق وحفظه قد تحتاج إلى إعادة تحميل الصّفحة كاملة بتركيبة المفاتيح CTRL+SHIFT+R وذلك لأنّ المُتصفّح يخبّئ الملف عند أول مرّة تزور فيها الصّفحة وبالتالي فسيعطيك نفس الملف غير المُعدّل في كلّ مرّة تعيد فيها تحميل الصّفحة، والحلّ هو بإعادة تحميل كاملة لجميع الملفّات.

ربط ملفات Javascript

مثلما هو عليه الحال مع ملفّات css يُمكنك ربط ملفّات جافاسكريبت بوضعها داخل وسم script واستخدام الدّالة url_for. إليك مثالا:

<script src="{{ url_for('static', filename='js/main.js')}}"></script>

مع مُلاحظة بأنّ ملفّ main.js يجب أن يكون داخل المُجلّد js وليس في مكان آخر.

عرض الصور

يُمكن كذلك استعمال الدّالة لعرض الصّور، كلّ ما عليك فعله هو وضع الصّورة داخل المُجلّد img وعرضها بالدّالة url_for داخل وسم img، مثلا ضع صورة حسب رغبتك داخل المجلّد img وسمّها logo.png

لعرضها أعلى الصّفحة يُمكن إضافة السّطر التّالي مُباشرة بعد وسم body.

<img src="{{ url_for('static', filename='img/logo.png')}}" />

بهذا سيُصبح التّطبيق كالتّالي: 

KBDVbdL.png

يُمكنك تصفّح ملفّات التّطبيق من هذا الرّابط.

خاتمة

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





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


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



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن