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

السّياق في مُحرّك القوالب Jinja


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

مُقدّمة

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

main.png

لتطبيق أمثلة هذا الدّرس، يُمكنك إنشاء تطبيق Flask صغير ليقوم بتقديم قوالب HTML أو يُمكنك استعمال إطار عمل آخر مثل Pyramid أو Django أو Bottle لكن سيتوجّب عليك البحث عن كيفيّة استعمال Jinja مع إطار العمل الذي تختاره.

السّياق في مُحرّك القوالب Jinja عند تضمين قالب في قالب آخر أو عند الاستيراد

يعني مُختلف المُتغيّرات والدّوال المُعرّفة في جزء مُعيّن من الشّفرة. فمثلا، إن عرّفت مُتغيّرا في الجزء العلوي من قالب HTML كما يلي:

{% set website = 'academy.hsoub.com' %}

بعد تعريف المُتغيّر website في القالب، فسيُصبح هذا المُتغيّر مُتاحا في هذا القالب فقط، ما يعني بأنّك لن تستطيع الوصول إلى المُتغيّر website في قالب HTML آخر دون استيراده.

السّياق عند تضمين قالب داخل قالب آخر

فلنضع السّيناريو التّاليَ لتتوضّح الفكرة أكثر، لنقل بأنّ لدينا ملفّات HTML التّالية:

  • base.html
  • index.html
  • url.html

مُحتويات ملفّ base.html ستكون كالتّالي:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    {% block body %}
    {% endblock %}
</body>
</html>

وملفّ index.html يرث من الملفّ base.html مع تعريف المُتغيّر website وتضمين الملفّ url.html باستعمال الجملة include:

{% extends 'base.html' %}

{% block title %} My Application {% endblock %}

{% block body %}

{% set website = 'academy.hsoub.com' %}

{% include 'url.html' %}

{% endblock %}

لنقل بأنّك تُريد الوصول إلى قيمة المُتغيّر website داخل الملفّ url.html لعرض الرّابط داخل وسم <a> كما يلي:

{# 'url.html' #}

<a href='http://{{ website }}'>{{ website }}</a>

ستكون النّتيجة كما يلي:

<a href='http://academy.hsoub.com'>academy.hsoub.com</a>

نجح الأمر لأنّ السّياق قد مُرِّر من الملفّ index.html إلى الملفّ url.html عند استخدام التعليمة include، ما يعني بأنّ المُتغيّرات المُعرّفة في الملفّ الأصلي ستكون مُتاحة في الملفّ الفرعي الّذي ضُمِّن بالجملة include.

لكنّك لن تستطيع الوصول إلّا إلى المُتغيّرات التي عُرّفت قبل استعمال الجملة include.
ما يعني بأنّك لو عرّفت المُتغيّر website بعد تضمين القالب url.html كما يلي:

{% include 'url.html' %}

{% set website = 'academy.hsoub.com' %}

فالقالب url.html لن يتعرّف على المُتغيّر website وسيعتبره سلسلة نصيّة فارغة لأنّه لا يستطيع الوصول إلى قيمته.
ستحصل بالتّالي على النّتيجة التّالية:

<a href='http://'></a>

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

تضمين قالب داخل قالب آخر باستعمال التعليمة include يُمرّر السّياق مبدئيا من قالب لآخر ، لكن إن أردت منع حدوث هذا الأمر، يُمكنك استعمال التّعبير without context لمنع القالب الفرعي من الوصول إلى سيّاق القالب الأصلي، وبالتّالي لن يتمكّن من الوصول إلى المُتغيّرات التّي سبق تعريفها.

لتجربة هذا، أضف التّعبير without context إلى صفحة index.html ليُصبح مُحتوى الملفّ كما يلي:

{% extends 'base.html' %}

{% block title %} My Application {% endblock %}

{% block body %}

{% set website = 'academy.hsoub.com' %}
{% include 'url.html' without context %}

{% endblock %}

إن ألقيت نظرة على النّتيجة، ستجدها مُشابهة لما كانت عليه عندما عرّفنا المُتغيّر website بعد التعليمة include ما يعني أنّ القالب url.html لم يعد مُؤهّلا للوصول إلى قيمة المُتغيّر، ولهذا نفس التّأثير مع جميع المُتغيّرات والدّوال المُعرّفة في السّياق.

السّياق عند الاستيراد

يُمرّر السّياق مبدئيا بين القوالب عند استعمال الجملة include، لكنّ العكس يحدث عند استيراد قالب داخل قالب آخر.

لتوضيح هذه الفكرة، لنُغيّر مُحتويات الملفّ url.html إلى الماكرو التّالي :

{% macro url() %}
    <a href='http://{{ website }}'>{{ website }}</a>
{% endmacro %}

لاحظ بأنّ الماكرو لا يقبل أية مُعاملات، لكنّنا نحاول أن نصل إلى المُتغيّر website داخل الماكرو.

لنُغيّر الآن التعليمة include إلى جملة import لاستيراد هذا الماكرو من القالب url.hmtl:

{% set website = 'academy.hsoub.com' %}

{% from 'url.html' import url %}
{{ url() }}

لاحظ بأنّنا نقوم بالاستيراد بعد تعريف المُتغيّر.

بما أنّ السّياق لا يُمرّر مبدئيا عند الاستيراد، فالنّتيجة ستكون كما لو أنّ قيمة المُتغيّر website فارغة.
استعمل التّعبير with context عند الاستيراد كما يلي، للتصريح برغبتك في تمرير السّياق :

{% from 'url.html' import url with context %}

التّعبير with context له تأثير مُعاكس لتأثير التّعبير without context، لذا فالمُتغيّر website سيُمرَّرُ إلى القالب url.html وبالتّالي فالماكرو url() يستطيع الوصول إلى قيمة المُتغيّر ومن ثمّ عرضه بصيغة رابط.

ملحوظة: المثال أعلاه مُجرّد توضيح لفكرة السّياق عند الاستيراد، ومن الأفضل استعمال المُعاملات لنقل قيمة مُتغيّر إلى ماكرو مُعيّن عوضا عن تفعيل السّياق.

الخلاصة أنّك تستطيع التّحكم في كيفيّة تعامل القوالب مع السّياق عند التضمين أو الاستيراد عبر استخدام كلّ من التّعبيرين with context و without context.

الحالة المبدئية للتعليمة include هي كالتّالي:

{% include 'url.html' with context %}

والحالة االمبدئية للتعليمة import:

{% import 'url.html' as url without context %}

تجاهل تضمين القوالب إن لم تكن متواجدة أساسا

سأعرض لك في هذه الفقرة بشكل وجيز تقنيتين من التّقنيات التّي ستُساعدك على تصميم قوالب HTML أفضل عند استخدام التعليمة include.

أولا، يُمكنك منح مُحرّك القوالب Jinja إمكانيّة تضمين قالب فقط إن كان موجودا عبر تمرير قائمة من أسماء القوالب إلى التعليمة include كالتّالي:

{% include ['page1.html', 'page2.html'] %}

إذا كان القالب page1.html موجودا فسيكون للمثال أعلاه نفس تأثير ما يلي:

{% include 'page1.html' %}

سواء أكان القالب page2.html موجودا أم لا فهذا غير مُهمّ ما دام القالب page1.html أول قالب في القائمة وكان موجودا بالفعل داخل مُجلّد templates.

أمّا في حالة لم يكن القالب page1.html موجودا داخل مُجلّد القوالب وكان القالب page2.html موجودا عوضا عنه، فهذا الأخير هو القالب الذي سيُضمّن.

وفي حالة لم يكن أي قالب من القوالب المُوفَّرة موجودا، فستحصل على خطأ كالتّالي:

jinja2.exceptions.TemplatesNotFound

TemplatesNotFound: none of the templates given were found: page1.html, page2.html

ولتجاهل هذا الخطأ وعرض مُحتويات القالب index.html رغم عدم وجود هذه القوالب المُضمَّنة، يُمكنك إضافة التّعبير ignore missing عند استعمال التعليمة include كالآتي:


{% include ['page1.html', 'page2.html'] ignore missing %}

التّعبير ignore missing ليس محصورا بقائمة قوالب فقط، بل تستطيع كذلك استخدامه عند تضمين قالب واحد فقط كما يلي:

{% include "sidebar.html" ignore missing %}

عند استعمال التّعبير، سيتحقّق مُحرّك القوالب Jinja من أنّ القالب sidebar.html موجود في مُجلّد القوالب، إن كان كذلك فعمليّة التّضمين ستكون ناجحة دون أخطاء، وإن لم يكن القالب موجودا فلن تحدث أية أخطاء وسيبقى مكان التعليمة include فارغا عند عرض النّتيجة.

تنبيه:

عند استخدام التّعبير ignore missing مع كلّ من التّعبيرين with context و without context في آن واحد، تأكّد من أنّ التّعبير ignore missing يأتي أولا.

ما يعني بأنّ الأمثلة أسفله صحيحة:

{% include ['page1.html', 'page2.html'] ignore missing with context %}

{% include "sidebar.html" ignore missing with context %}

{% include "sidebar.html" ignore missing without context %}

والأمثلة التّاليّة خاطئة:

{% include ['page1.html', 'page2.html'] with context ignore missing %}

{% include "sidebar.html" with context ignore missing %}

{% include "sidebar.html" without context ignore missing %}

خاتمة

تعرّفنا في هذا الدّرس على كيفيّة عمل السّياق في Jinja عند التّضمين والاستيراد، بعد قراءتك لهذا الدّرس، يجب أن تكون قادرا على إدارة السّياق باحترافيّة وتتجنّب المشاكل التي تقع في حالة إساءة فهم كيفيّة عمل السّياق، في الدّرس التّالي، سنتعرّف على تقنيات متقدّمة للتّعامل مع حلقات for من أجل تحسين تطبيقاتك وتكتب شفرة أكثر فعاليّة عند تطوير تطبيقات الويب باستعمال إطار العمل Flask أو أي إطار عمل يعتمد على Jinja.


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

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

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



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.


×
×
  • أضف...