القوالب

في حين أنَّ فلاسك لا يجبرك على استخدام أيَّة لغة قوالب محددة، فهو يفترض أنك ستستخدم لغة جينجا (Jinja). معظم المطورين في مجتمع فلاسك يستخدمون جينجا، وأنصحك بأن تفعل المِثل. يوجد بضعة إضافات (ملحقات) تتيح لنا استخدام لغات قوالب أخرى، مثل Flask-Genshi و Flask-Mako. استخدم الخيار الافتراضي (جينجا) ما لم يكن لديك سبب جيد لتستخدم شيء آخر. عدم معرفتك للبنية الكتابية لجينجا ليس بالسبب الجيد! فالسوف توفر الكثير من الوقت والصداع.

ملاحظة
معظم المواقع تَقصُد Jinja2 في حين أنها تكتب "Jinja" وحسب. حيث كان هناك إصدار أول من هذه اللغة (Jinja1)، ولكننا لن تعامل معه هنا. عندما سأذكر جينجا في هذا الكتب، فسأكون أقصد هذا الإصدار.

مقدمة سريعة حول جينجا

يؤدي توثيق جينجا عملاً جيداً في شرح البنية الكتابية وميزات هذه اللغة. لن أقوم بإعادة المعلومات المذكورة هناك، ولكن أريد أن أحرص على رؤيتك لهذه الملاحظة الهامة:

يوجد نوعان من المحددات في اللغة: {% ... %} و {{ ... }}. يُستخدم الأول لتنفيذ الجمل (Statements) مثل حلقة for أو لإسناد القيم، أما الثاني فيُستخدم لطباعة نتيجة تعبير إلى القالب.

توثيق مُصمم قوالب جينجا

كيفية تنظيم القوالب

أين المكان المناسب لوضع القوالب في تطبيقنا؟ إذا كنت قد تابعت الدورة منذ البداية، فمن المؤكد أنك لاحظتَ أنَّ إطار فلاسك يملك مرونة عالية فيما يتعلق بتنظيم الأشياء. ولربما لاحظت أيضاً أنَّ هناك عادةً مكان موصى به لوضع تلك الأشياء. يوصى بوضع القوالب في دليل الحزمة.

myapp/
    __init__.py
    models.py
    views/
    templates/
    static/
run.py
requirements.txt
templates/
    layout.html
    index.html
    about.html
    profile/
        layout.html
        index.html
    photos.html
    admin/
        layout.html
        index.html
        analytics.html

يماثل تنظيم الدليل templates تنظيم موجهاتنا. حيث أنَّ القالب الذي سيُعرض في العنوان myapp.com/admin/analytics هو templates/admin/analytics.html. كما يوجد بعض القوالب الإضافية التي ستُستَمَد ولكن لن يتم عرضها بشكل مباشر. بحيث أنَّ ملفات layout.html سيتم وراثتها (استمدادها/تضمينها) من قوالب أخرى.

الوراثة

يعتمد دليل القوالب الُمنظم بشكل جيد بشدة على الوراثة. فيُعرِّف القالب الأب عادةً الهيكل العام الذي سترثه جميع القوالب الأبناء. في مثالنا السابق، يعد القالب layout.html هو القالب الأب، والملفات البقيّة التي تحمل الإمتداد html. هي القوالب الأبناء.

ستملك عادةً ملف layout.html رئيسي (ذا مستوى أول) يُعرِّف التخطيط (التصميم/التنظيم) العام لتطبيقك، وملف آخر لكل قسم من موقعك. إذا قمت بأخذ نظرة على الدليل أعلاه، ستجد أنَّ هناك قالب رئيسي (myapp/templates/layout.html) كما توجد قوالب رئيسية فرعية مثل myapp/templates/profile/layout.html و myapp/templates/admin/layout.html. القالبان الفرعيان يرثان ويعدلان الأول (القالب ذو المستوى الأول).

يتم تنفيذ الوراثة باستخدام الوسمان {% extends %} و {% block %}. حيث يمكننا تعريف كتل في القالب الأب ليتم ملؤها (الكتابة بداخلها) بواسطة القوالب الأبناء.

{# _myapp/templates/layout.html_ #}

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>{% block title %}{% endblock %}</title>
    </head>
    <body>
    {% block body %}
        <h1>This heading is defined in the parent.</h1>
    {% endblock %}
    </body>
</html>

الآن يمكننا استمداد (extend) القالب الأب في القالب الابن وتعريف محتوى (ملء) هذه الكتل.

{# _myapp/templates/index.html_ #}

{% extends "layout.html" %}
{% block title %}Hello world!{% endblock %}
{% block body %}
    {{ super() }}
    <h2>This heading is defined in the child.</h2>
{% endblock %}

تتيح لنا الدالة ()super تضمين أياً كان ما بداخل هذه الكتلة في القالب الأب.


قم بزيارة وثيقة جينجا عن الوراثة في القوالب للمزيد من المعلومات.

إنشاء وحدات ماكرو

يمكننا تطبيق مبدأ لا تكرر نفسك في القوالب عن طريق استخلاص المقاطع البرمجية التي ستُستخدم بشكل متكرر وتحويلها إلى وحدات ماكرو (Macros). على سبيل المثال، إن كنا نعمل على الشريط العلوي لتطبيقنا، فقد نرغب بإعطاء صنف (Class) مختلف للرابط "النشط" (أي رابط الصفحة الحالية). من دون استخدام وحدات الماكرو، سنضطر إلى استخدام العبارة if ... else للتحقق من كل رابط لإيجاد النشط منهم.

توفر وحدات الماكرو طريقة لتجزئة الشيفرة البرمجية، فهي تعمل كالدوال. دعنا نلقي نظرة على طريقة يمكننا استخدامها لتحديد الرابط النشط باستخدام الماكرو.

{# myapp/templates/layout.html #}

{% from "macros.html" import nav_link with context %}
<!DOCTYPE html>
<html lang="en">
    <head>
    {% block head %}
        <title>My application</title>
    {% endblock %}
    </head>
    <body>
        <ul class="nav-list">
            {{ nav_link('home', 'Home') }}
            {{ nav_link('about', 'About') }}
            {{ nav_link('contact', 'Get in touch') }}
        </ul>
    {% block body %}
    {% endblock %}
    </body>
</html>

ما قُمنا به في هذا المثال هو استدعاء ماكرو غير مُعرَّف (وهو الماكرو nav_link) وتمرير معاملين له: الأول اسم الدالة الهدف (أي اسم الدالة لدالة العرض المستهدفة)، والثاني النص الذي نريد إظهاره.

ملاحظة
قد تكون لاحظت أننا استخدامنا with context في عبارة الاستدعاء. يتكون سياق (Context) جينجا من مجموعة المعاملات المُمررة إلى دالة ()rendrer_template وإلى سياق بيئة جينجا (Jinja environment context) من الشيفرة البرمجية. تُصبح هذه المتغيرات متوفرة في القالب الذي يجري عرضه.

بعض المتغيرات تُمرر بشكل جلي بواسطتنا، مثل render_template("index.html", color="red")، ولكن يوجد بعض المتغيرات والدوال تُضمَّن تلقائياً بواسطة إطار فلاسك، مثل request و g و session. فعندما نستخدم {% from ... import ... with context %} نحن نخبر مُحرِّك جينجا أن يجعل جميع هذه المتغيرات متوفر أيضاً في الماكرو.
ملاحظة
  • يمكنك إيجاد جميع المتغيرات العمومية (global) المُمرّرة إلى سياق جينجا تلقائياً من هنا.
  • يمكننا تعريف دوال ومتغيرات نريد دمجها في سياق جينجا باستخدام معالجات السياق.

الآن حان وقت تعريف الماكرو nav_link الذي استخدمناه في ذلك القالب.

{# myapp/templates/macros.html #}

{% macro nav_link(endpoint, text) %}
{% if request.endpoint.endswith(endpoint) %}
    <li class="active"><a href="{{ url_for(endpoint) }}">{{text}}</a></li>
{% else %}
    <li><a href="{{ url_for(endpoint) }}">{{text}}</a></li>
{% endif %}
{% endmacro %}

قمنا بتعريف الماكرو في المسار myapp/templates/macros.html. في هذا الماكرو قمنا باستخدام الكائن request - والذي يكون متوفر إفتراضياً في سياق جينجا - للتأكد مما إذا كان الطلب الحالي قد وُجِّهَ إلى الدالة المُمرّرة إلى الماكرو nav_link. إن كان، فبالتالي نحن في تلك الصفحة ويمكننا تعليم رابطها كرابط نشط.

ملاحظة
عبارة from x import y تأخذ القيمة x كمسار نسبي. بحيث إذا كان قالبنا في المسار myapp/templates/user/blog.html فسنستخدم from "../macros.html" import nav_link with context.

مُرشِّحات مخصصة

مُرشِّحات جينجا هي عبارة عن دوال يُمكِن تطبيقها على نتائج العبارة في المحدد {{ ... }}. بحيث تُطبَّق هذه المُرشِّحات قبل طباعة هذه النتائج في القالب.

<h2>{{ article.title|title }}</h2>

في المثال أعلاه، المُرشِّح title سيأخذ article.title ويعيد النسخة المعنونة حرفياً (title cased) منه، والتي من ثم ستُطبَع إلى القالب. تشبه آلية عمل هذه الخاصية إلى حدٍ كبير آلية عمل "ضخ (piping)" مُخرج برنامج إلى مدخلات برنامج آخر في أنظمة يونكس.

ملاحظة
توجد حفنة من المُرشِّحات المبنيّة ضمنياً في جينجا من مثل title. يمكنك رؤية القائمة الكاملة في توثيقات جينجا.

يمكننا تعريف مُرشِحات خاصة بنا لاستخدامها في قوالب جينجا. كمثال، سنقوم بتنفيذ مُرشِّح بسيط باسم caps يقوم بجعل جميع أحرف السلسلة النصيّة كبيرة.

ملاحظة
يملك محرك جينجا بالفعل مُرشِّح يسمى upper وهو يؤدي تلك الوظيفة، كما يملك المُرشِّح capitalize الذي يقوم بجعل الحرف الأوّل بالحالة الكبيرة وباقي الأحرف بالحالة الصغيرة. تتعامل هذه المُرشِّحات أيضاً مع تحويلات اليونيكود (Unicode)، ولكننا سنبقي المثال بسيطاً لتسليط الضوء على الفكرة.

سنقوم بتعريف المُرشِّح في وحدة تتوضع في المسار myapp/util/filters.py. أي سيصبح لدينا الحزمة util والتي سنضع فيها الوحدات الأخرى المتنوعة.

# myapp/util/filters.py

from .. import app

@app.template_filter()
def caps(text):
    """Convert a string to all caps."""
    return text.uppercase()

في هذا المثال قمنا بتسجيل الدالة كمُرشِّح جينجا باستخدام المُزخرِف ()app.template_filter@. يأخذ المُرشِّح اسمه الافتراضي من اسم الدالة، ولكن يمكننا تغيير ذلك عن طريق تمرير معامل للمُزخرِف.

@app.template_filter('make_caps')
def caps(text):
    """Convert a string to all caps."""
    return text.uppercase()

الآن يمكننا استدعاء المُرشِّح بالاسم make_caps في القالب بدلاً من استخدام caps كالتالي: {{ "hello world!"|make_caps }}.

لجعل المرشح متاحاً في القوالب، كل ما علينا القيام به هو استدعاءه في ملف init__.py__ الرئيسي.

# myapp/__init__.py

# تأكد أنه تم استهلال التطبيق أولاً لمنع مشاكل الاستيرادات الحلقية.
from .util import filters

الخلاصة

  • استخدم جينجا للتعامل مع القوالب.
  • يوجد نوعان من المحددات في محرك جينجا: {% ... %} و {{ ... }}. يُستخدم الأول لتنفيذ الجمل (Statements) مثل حلقة for أو لإسناد القيم، أما الثاني فيُستخدم لطباعة نتيجة تعبير إلى القالب.
  • ينصح بأن توضع القوالب في الدليل myapp/templates/ أي في دليل بداخل حزمة التطبيق.
  • أنصح بأن تعكس هيكلة الدليل templates/ هيكلة عناوين الروابط في التطبيق.
  • أنصح بأن تملك ملف layout.html رئيسي في الدليل myapp/templates وملف خاص لكل قسم في الموقع. وعليك أن تحرص على أن يستدعي الملف المخصص لقسم معين الملف الرئيسي.
  • وحدات الماكرو هي مثل الدوال مصنوعة من شيفرة القالب.
  • المُرشِّحات هي دوال مصنوعة من شيفرة بايثون وتستخدم في القوالب.

results matching ""

    No results matching ""