11  صياغة قوالب الصفحات وصب البيانات فيها

قوالب جنجا (Jinja)

مكتبة جنجا (Jinja) تبرمج عمليَّة صياغة البيانات كمحتوى HTML. إذْ نعرِّف قوالب ونصب فيها البيانات فتخرج لنا صفحة إلكترونية قابلة للإرسال للمتصفِّح.

مكتبة جنجا تصف نفسها بثلاثة خصائص:

  1. سريع
  2. معبر
  3. قابل للتوسيع

مثال لتعريف قالب (Template) فيه استعمال التوليد بالتكرار واستبدال أجزاء النص بقيمة المتغيرات المذكورة فيها:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My Webpage</title>
  </head>
  <body>
    <ul id="navigation">
      {% for item in navigation %}
      <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
      {% endfor %}
    </ul>

    <h1>My Webpage</h1>
    {{ a_variable }}
  </body>
</html>

سنحفظ هذا القالب في ملف نسميه: jinja-demo.html ونضعه داخل مجلد نسميه templates مثلاً.

ثم نستورِد جنجا من الوحدة الخاصة بها:

from starlette.templating import Jinja2Templates

ثم ننشئ لها كائنًا نسميه templates:

# See: https://starlette.dev/templates/
templates = Jinja2Templates(directory="templates")

ثم نستعمل الطريقة templates.TemplateResponse ونمرر البيانات التي تكون نتيجة للمعالجة في القاموس context لإعطاء القيَم أسماء يتم استعمالها في القالب:

from starlette.applications import Starlette
from starlette.routing import Route
from starlette.templating import Jinja2Templates


# Starlette is not strictly coupled to any particular templating engine, but Jinja2 provides an excellent choice.
# Starlette provides a simple way to get jinja2 configured. This is probably what you want to use by default.
# See: https://starlette.dev/templates/
templates = Jinja2Templates(directory="templates")


def homepage(request):
    return templates.TemplateResponse(
        request,
        "jinja-demo.html",
        context={
            "a_variable": "a value",
            "navigation": [
                {"href": "/", "caption": "Home"},
                {"href": "/about", "caption": "About"},
                {"href": "/contact", "caption": "Contact"},
            ],
        },
    )


routes = [
    Route("/", endpoint=homepage),
]

app = Starlette(debug=True, routes=routes)

جرب: تدرب على إنشاء قوالب، ثم تعريف دوال تشبه homepage التي هنا، وذلك لكل من صفحتي: /about و /contact على نحو ما في المثال السابق.

راجع صفحة توثيق مصص القوالب لمعرفة كيفية كتابة القوالب.

ومن مميزات جينجا أن فيها تركيب للعناصر بما يسمى Macros.

تعبئة عناصر الإدخال

يتم تعيين القيمة الافتراضية للمدخل عبر الخاصية value، وهي المحل المناسب لتعبئة القيمة الابتدائية لعناصر الإدخال:

<input type="text" name="first_name" value="{{ first_name }}">

ويجب مراعاة النمط الذي يتبعه هذا الحقل، بحسبه، فالتواريخ لها صيَغ مقبولة محددة، والمرجع فيه إلى التوثيق على شبكة موزيلا للمطورين تحت العنوان (value) لمعرفة ذلك، حيث يتبيَّن أن النمط هو:

yyyy-mm-dd

أي: من اليسار إلى اليمين: السنة (4 أحرف) ثم الشهر (حرفان) ثم اليوم (حرفان). مثل:

  • 2019-01-13
  • 1993-11-13
  • 0571-04-13 (سنة 571م)

ولذلك تجد في بايثون دالة لصياغة الوقت (date.strftime)، يمكن استعمالها لملئ القيمة بالشكل المناسب لها:

<input
    type="date"
    name="event_date"
    value="{{ event.date.strftime("%Y-%m-%d") }}"
>

## تعبئة عنصر إدخال النص متعدد الأسطر

أما في حالة عنصر إدخال النص متعدد الأسطر (<textarea>)، فإن تعبئة القيمة لا تتم باستخدام السمة value، بل يتم وضع القيمة بين وسمي الفتح والإغلاق للعنصر.

<textarea name="body" rows="10" cols="50">
{{ post.body }}
</textarea>

تعبئة عناصر الاختيار

تذكَّر أن الاختيار المنفصل (إما أو) يكون بنوع الإدخال radio، والربط بينها حتى يتم إزالة الأُخَر عند اختيار واحدة منها= يكون باشتراكها في خاصية الاسم name.

أما العنصر المختصار ابتداءً، فنضع عنده الخاصية checked وذلك بجملة الشرط if، على النحو التالي في جميعها:

<label>
    <input type="radio" name="subscription_type" value="free"
    {% if user_data.subscription_type == 'free' %}checked{% endif %}>
    Free Tier
</label>

<label>
    <input type="radio" name="subscription_type" value="paid"
    {% if user_data.subscription_type == 'paid' %}checked{% endif %}>
    Paid Tier
</label>

وكذلك الحال في الاختيار المتعدد checkbox:

وسنكتبه بحلقة تكرار، اختصارًا، على هذا النحو:

{% for category in available_categories %}
    <label>
        <input type="checkbox" name="categories" value="{{ category.id }}"
        {% if category.id in selected_ids %}checked{% endif %}>
        {{ category.name }}
    </label>
<br>
{% endfor %}

أما عنصر الاختيار <select> فهو يختلف قليلاً عن عناصر الإدخال <input> لكنه يعتبر منها وإن لم يكن أحد أنواعها.

وطريقة كتابته تكون بتضمين عناصر <option> بقيمة value ومحتوًى داخل العنصر ليظهر كنص اختيار في قائمة الخيارات عند الضغط على العنصر <select> نفسه.

ثم الخاصية selected تكون موجودة في إحدى عناصر <option> ويتم وضعها بحسب مطابقتها للاختيار ابتداءً، بجملة الشرط if داخل حلقة التكرار for على النحو التالي:

<select name="country_code">
{% for code, name in available_countries.items() %}
    <option value="{{ code }}"
    {% if user_data.country_code == code %}
    selected
    {% endif %}
    >
        {{ name }}
    </option>
{% endfor %}
</select>