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

حلقة التّكرار for في محرك القوالب Jinja


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

مُقدّمة

تعرّفنا سابقا على حلقة التّكرار for للدّوران على عناصر قائمة أو مفاتيح وقيم قاموس بايثون، سنتعرّف الآن على تقنيات مُتقدّمة ستُساعدك على التّعامل مع حلقة for على نحو أفضل لتتمكّن من تصميم قوالب HTML مع مُحرّك القوالب Jinja بطريقة أسرع وأكثر إتقانا.

main.png

استعمال حلقة for مع قائمة بايثون

تُستعمل حلقة for مع قوائم بايثون عاديّة كما يلي:

{% set names = ['Ahmed', 'Khalid', 'Ali', 'Abdelhadi', 'Yasser'] %}

<ul>
{% for name in names %}
    <li>{{ name }}</li>
{% endfor %}
</ul>

النّتيجة:

Ahmed 
Khalid 
Ali 
Abdelhadi 
Yasser 

بهذه الطّريقة، يُمكنك عرض كل عنصر من القائمة داخل وسم HTML مُعيّن وتطبيق تنسيق باستخدام CSS كما تشاء.

استعمال حلقة for مع قواميس بايثون

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

{% set website = { 'url': 'academy.hsoub.com',
                   'company': 'Hsoub',
                   'description': 'Learn new skills for free'
                 }
%}

<ul>
{% for key, value in website.iteritems() %}

    <li> {{ key }} : {{ value }} </li>

{% endfor %}

</ul>

لاحظ بأنّنا نقوم بالوصول إلى محتويات القاموس عبر تطبيق التّابع iteritems() على المُتغيّر website، بحيث نحصل على المفتاح والقيمة عبر المُتغيّرين key و value على التّوالي، داخل حلقة for نقوم بعرض كلّ من المفتاح وقيمته داخل وسم <li>.

النّتيجة:

    url : academy.hsoub.com

    company : Hsoub

    description : Learn new skills for free

يُمكنك كذلك استخدام تنسيقات إطار العمل Bootstrap لعرض مُحتويات قاموس ما بشكل جميل.

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

{% set post = {'title': 'Introduction to Flask',
               'comments': ['Great!', 'Thank you!']
               }

مثال تطبيقي

لنجمع الآن الأفكار السّابقة ولنُصمّم صفحة HTML بسيطة لعرض معلومات شخص ما:


{% set person = { 'first_name': 'Ali',
               'last_name': 'Sayed',
               'age': '27',
               'languages': ['Arabic', 'English'],
               'Children': ['Hassan', 'Fatima'],
             }
%}


<div class="col-sm-4">

<ul class="list-group">
  <li class="list-group-item active">
    {{ person['first_name']}} {{ person['last_name']}}
  </li>
  {% for key, value in person.iteritems() %}
        {% if value is string %}
         <li class="list-group-item">{{ key }} : {{ value }}</li>
        {% else %}
         <li class="list-group-item">
             {{ key }} : {% for item in value %} {{ item }}, {% endfor %}
        {% endif %}
         </li>
  {% endfor %}
</ul>

</div>

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

إليك ما يحدث في الشّفرة أعلاه، أولا نُعرّف قاموسا باسم person ليحمل المفاتيح التّالية:

  • الاسم الأول للشّخص،
  • الاسم العائلي (النّسب)،
  • العمر،
  • اللغات التي يُتقنها الشّخص،
  • أطفاله.

لاحظ بأنّ قيم اللغات والأطفال عبارة عن قوائم بايثون.

في شفرة HTML نقوم بالدّوران حول مُحتويات القاموس person، بعدها نستعمل الاختبار string المُعرّف قياسيا في Jinja للتّحقق ممّا إذا كانت القيمة عبارة عن سلسلة نصيّة أو لا، إن كانت كذلك فإنّنا نعرضها مع مفتاحها بشكل طبيعي، إن لم تكن القيمة سلسلة نصيّة فهذا يعني بأنّها قائمة من عناصر مُتعدّدة وأنّ الجزء الذي يلي الكلمة المفتاحيّة else هو الذي سيُعرَض.

في الجزء else نقوم بعرض المفتاح ثمّ نستعمل الحلقة for مُجدّدا للدّوران على عناصر القائمة المُتواجدة في قيمة المفتاح المذكور آنفا.

هذا هو الجزء الخاص بعرض عناصر قائمة بعد قيمة المفتاح:

{% else %}
    <li class="list-group-item">
        {{ key }} : {% for item in value %} {{ item }}, {% endfor %}

إن استخدمت إطار العمل Bootstrap فستكون النّتيجة مُشابهة لما في الصّورة التّاليّة:

01_person_info.png

لاحظ بأنّ القاموس غير مُرتّب على نحو صحيح، ذلك لأنّ قواميس بايثون ليست مُرتّبة مبدئيًّا، لكن يُمكنك ترتيب مُحتويات القاموس أبجديا عبر استعمال المُرشّح dictsort كما يلي:

{% for key, value in person | dictsort %}

مع مُلاحظة أنّك لن تحتاج إلى التّابع iteritems() عند استعمال المُرشّح.

المُتغيّر loop عند استخدام الحلقة for

حلقة for تعمل بطريقة بسيطة، إذ تأخذ عدّة قيم وتمنحك كل قيمة داخل مُتغيّر مؤقّت، وفي كلّ دورة تتغيّر قيمة المُتغيّر.

يوفّر مُحرّك القوالب Jinja عند التّعامل مع حلقة for مُتغيّرا خاصّا داخل الحلقة باسم loop؛ باستعمال هذا المُتغيّر الخاص، يُمكنك الوصول إلى معلومات خاصّة بالدورة الحاليّة وتُمكّننا كذلك من استعمال تقنيّات الاستدعاء الذّاتي (Recursion) لتضمين عناصر قائمة داخل عناصر قائمة أخرى خاصّة بالدّورة الحاليّة (ستتضّح الصّورة أكثر عندما ننتقل إلى الأمثلة التّطبيقية).

الوصول إلى رقم الدّورة الحاليّة

يُمكنك استخدام الخاصيّة index مع المُتغيّر loop في الحلقة for كما يلي:

{% for item in items %}
    {{ loop.index }}
{% endfor %}

مثال:

{% set list = ['Python', 'Flask', 'Jinja',
               'Programming', 'Web developement'] %}

{% for item in list %}
   <p>{{ loop.index }} - {{ item }}</p>
{% endfor %}

النّتيجة:

1 - Python

2 - Flask

3 - Jinja

4 - Programming

5 - Web development

لاحظ بأنّ العدّ يبدأ من واحد، إن أردت أن يبدأ العدّ من الصّفر، سيتوجّب عليك استخدام التّابع index0 عوضا عن التّابع index كما يلي:

{{ loop.index0 }}

بهذا التّعديل ستكون النّتيجة كما يلي:

0 - Python

1 - Flask

2 - Jinja

3 - Programming

4 - Web developement

الوصول إلى رقم الدّورة الحاليّة عكسيّا

يُمكنك كذلك عكس تأثير التّابع index عبر استعمال التّابع revindex كالتّالي:

{{ loop.revindex }}

النّتيجة:

5 - Python

4 - Flask

3 - Jinja

2 - Programming

1 - Web developement

وللانتهاء بالرّقم 0 يُمكنك استعمال التّابع revindex0.

التحقّق مما إذا كانت الدّورة الحاليّة هي الأولى أو الأخيرة

يُمكنك استعمال التّابع first الذي يُرجع القيمة True إذا كانت الدّورة الحاليّة هي أول دورة في حلقة for.

وبنفس الطّريقة فإنّ التّابع last يُساعد على التّحقّق ممّا إذا كانت الدّورة الحاليّة هي الأخيرة أو لا.

مثال:

{% for item in list %}
    {% if loop.first %}
        <p>{{ loop.index }} (First) - {{ item }}</p>
    {% elif loop.last %}
        <p>{{ loop.index }} (Last) - {{ item }}</p>
    {% else %}
        <p>{{ loop.index }} - {{ item }}</p>
    {% endif %}
{% endfor %}

النّتيجة:

1 (First) - Python

2 - Flask

3 - Jinja

4 - Programming

5 (Last) - Web developement

الوصول إلى عدد عناصر القيم الإجمالي

بالإضافة إلى إمكانيّة الحصول على عدد عناصر قائمة ما باستعمال المُرشّح count أو المُرشّح length، يُمكنك كذلك استخدام loop.length للحصول على عدد عناصر القيم التي اِستُخْدِمَتْ مع حلقة for الحاليّة.

يُمكنك التّحقق من الأمر عبر المثال التّالي (على افتراض أنّك تستعمل القائمة list السّابقة):

<p>{{ list | length }}</p>
<p>{{ list | count }}</p>
{% for item in list %}
   <p>{{ loop.length }} - {{ item }}</p>
{% endfor %}

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

إعادة استعمال نفس الحلقة مع قائمة داخل قائمة أخرى

لنفترض بأنّنا نُريد عرض أسماء أشخاص مع أبنائهم بحيث تكون قائمة HTML الخاصّة بالأبناء تابعة للأب.

إليك البيانات التّي سنستعملها في مثالنا هذا:

{% set people = [ dict(name='Ali', children=[dict(name='Mohammed'), dict(name='Hassan')]),
                 dict(name='Khaled', children=None),
                 dict(name='Fahd', children=[dict(name='Kamal'), dict(name='Omar')])
               ]
%}

في القائمة أعلاه، لدينا ثلاثة قواميس، القاموس الأول خاصّ بعلي، وأطفاله (الذين يعتبرون قائمة من القواميس كذلك) محمد وحسن، أما القاموس الثّاني فخاص بخالد الذي ليس لديه أبناء، والقاموس الأخير خاص بفهد، وأبناؤه كمال وعمر.

لعرض أسماء الآباء الذين يتواجدون في القواميس الثّلاثة، يُمكن أن نستخدم حلقة for بسيطة، لكن سيتوجّب علينا تكرار الشّفرة إذا ما أردنا الدوران حول أبناء كل شخص.

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

{% for person in people recursive %}

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

{% if person.children is not none %}
    <ul class="list-group"> {{ loop(person.children) }} </ul>
{% endif %}

المثال النّهائيّ سيكون كالتّالي:

{% set people = [ dict(name='Ali', children=[dict(name='Mohammed'), dict(name='Hassan')]),
                 dict(name='Khaled', children=None),
                 dict(name='Fahd', children=[dict(name='Kamal'), dict(name='Omar')])
               ]
%}

<div class="col-sm-4">
    <ul class="list-group">
        {% for person in people recursive %}
            <li class="list-group-item"> {{ person.name }}
        {% if person.children is not none %}
            <ul class="list-group"> {{ loop(person.children) }} </ul>
        {% endif %}
            </li>
        {% endfor %}
    </ul>
</div>

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

02_children.png

يُمكنك كذلك معرفة عمق الحلقة باستخدام التّابع loop.depth داخل حلقة for من النّوع recursive كالتّالي:

<span class="badge">{{ loop.depth }} </span>

تأكّد فقط من أن تضع السّطر السّابق مُباشرة بعد السّطر:

{% for person in people recursive %}

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

03_children_with_depth.png

لاحظ بأنّ رقم العمق يبدأ من واحد، إن أردت أن يبدأ من الصّفر فاستعمل loop.depth0.

يُمكنك كذلك استخدام هذه الميّزة مع تعليقات في مُدوّنة أو منصّة تتطلّب تفاعل الأعضاء بين بعضهم البعض.
الصّورة التّاليّة توضيح بسيط للنّتيجة النّهائيّة التي يُمكنك أن تصل إليها إن كنت تمتلك تعليقات متداخلة في قاعدة بياناتك:

04_comments.png

خاتمة

تعرّفنا في هذا الدّرس على كيفيّة استخدام حلقة for في مُحرّك القوالب Jinja للحصول على قوالب HTML أحسن وتطويرها بسرعة. وبهذا نكون قد أكملنا أساسيّات دروس مُحرّك القوالب Jinja وسننتقل في الدّروس القادمة للتّعرف على جزء آخر مُهمّ في تطوير الويب، ألا وهو كيفيّة التّعامل مع قواعد البيانات في التّطبيقات المكتوبة بإطار العمل Flask.


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

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

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



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

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

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

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


×
×
  • أضف...