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

التّعامل مع مكتبة WTForms ضمن إطار عمل Flask: حقول الخيارات المُتعدّدة


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

مُقدّمة

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

main.png

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

إنشاء وتقديم قائمة مُنسدلة

سننظر في هذا الجزء إلى كيفيّة إنشاء قائمة مُنسدلة لتوفير حقل اختيار للمُستخدم.

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

MA => المغرب
AU => أستراليا
BH => البحرين
DZ => الجزائر

ولإنشاء القائمة المُنسدلة فإنّنا نقوم باستيراد الصّنف SelectField من مكتبة WTForms ونقوم بإنشاء مُتغيّر من هذا الصّنف كما سبق، الاختلاف هنا هو أنّنا نُمرّر الاختيارات إلى مُعامل باسم choices أثناء تعريف المُتغيّر:
في المثال التّالي، نقوم بإنشاء حقل خيارات مُتعدّدة ليختار المُستخدم لغة برمجة مُعيّنة:

from wtforms import SelectField

class PastebinEntry(Form):
    language = SelectField(
            u'Programming Language', 
            choices=[('cpp', 'C++'),
                     ('py', 'Python'),
                     ('rb', 'Ruby')]
            )

يُمكنك أن تُلاحظ بأنّ ما نُمرّره إلى المُعامل choices عبارة عن قائمة بمجموعات من عنصرين، وكما قلت سابقا، فالعنصر الأول هو ما سيصلك عندما يُرسل المُستخدم البيانات، أمّا العنصر الثّاني فسيظهر للمُستخدم لذا إن اختار المُستخدم الخيار Python فما سيصلك أنت عند جانب الخادوم هو السّلسلة النّصيّة 'py'.

بعد إنشاء كائن من هذا الصّنف وتقديمه إلى ملفّ HTML فستتمكّن من تقديم الخيارات في صفحة HTML بنفس الطّريقة التّي كنّا نعرض بها الحقول سابقا:

{{form.language.label}}
<br>
{{form.language}}
    {% if form.language.errors %}
           <ul class=errors>
           {% for error in form.language.errors %}
               <li>{{ error }}</li>
           {% endfor %}
           </ul>
    {% endif %}

ولتفهم سبب توفير قيمتين لكل خيار، فتخيّل أنّ الخيارات طويلة جدا، ولو اخترت مثلا الخيار Python فإنّ قيمة form.language.data ستكون py وبالتّالي ستستطيع مُقارنتها مع قيمة أخرى بسهولة وبشيفرة أقصر.

حقل الاختيارات المُتعدّدة

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

from wtforms import SelectMultipleField

أمّا طريقة توفير الخيارات فهي نفسها الطّريقة المُتّبعة لإنشاء قائمة منسدلة عاديّة، استبدل فقط الصّنف SelectField بالصّنفSelectMultipleField:

language = SelectMultipleField(
    u'Programming Language', 
    choices=[('cpp', 'C++'),
    ('py', 'Python'),
    ('rb', 'Ruby')]
    )

وطريقة تقديم النّموذج هي نفسها، مع إمكانيّة توفير عدد الخيارات بتمريره إلى الخاصيّة size في لغة HTML ليكون الحقل أكثر تناسقا، وطريقة توفير عدد الخيارات هو كما يلي:

{{form.language(size=3)}}

هكذا ستتمكّن من اختيار أكثر من خيار واحد بجرّ فأرة الحاسوب أو الضّغط بشكل مُستمرّ على مفتاح CTRL من لوحة المفاتيح أثناء الاختيار.

عندما تُرسل الخيارات فإنّها تكون داخل قائمة بايثون في form.language.data ، وتستطيع الوصول إليها والتّعامل معها كما تتعامل مع قائمة عاديّة.

أزرار الانتقاء

أزرار الانتقاء أو Radio buttons تُشبه كثيرا القائمة المُنسدلة، إذ أنّها مُجرّد طريقة أخرى لتوفير خيارات للمُستخدم، إذ تُوفّر سؤالا للمُستخدم وتطلب منه الإجابة باختيار أحد الخيارات، ويُعتبر هذا الحقل مناسبا للأسئلة التّي تقبل جوابًا واحدًا مع توفير خيارات كإجابات مُحتملة (مثل طرح سؤال وتمكين المُستخدم من الإجابة بنعم أو لا عبر اختيار الإجابة المرغوبة).

يُمكنك إنشاء حقل لأزرار انتقاء عبر الصّنف RadioField الذي يُمكنك استيراده كذلك من مكتبة WTForms، والتّالي مثال على كيفيّة إنشاء حقل به خياران، Yes و No:

radio = SelectField(
 u'Are you happy?',
 choices=[('yes', 'Yes'), ('no', 'No')]
)

مُجدّدًا، العنصر الأول هو ما سيُرسل إلى الخادوم، أمّا العنصر الثّاني فهو ما يُعرض للمُستخدم.

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

{% for subfield in form.radio %}
    <tr>
        <td>{{ subfield }}</td>
        <td>{{ subfield.label }}</td>
    </tr>
{% endfor %}

وكما تُلاحظ، فإنّنا نعرض الزرّ ثمّ اللصيقة Label الخاصّة به.

مُربّع التأشير Check Box

تُستخدم مُربّعات التّأشير لتمكين المُستخدم من اختيار حقل مُعيّن ليبقى افتراضيّا، مثل مُربّع “تذكّرني” تحت نماذج تسجيل الدّخول، ولأنّه يُعنى بالحصول على المُوافقة أو الرّفض من المُستخدم، فهذا يعني بأنّه يقبل قيمتين منطقيّتين فقط، إمّا أن يؤشّر عليه المُستخدم (أي القيمة المنطقيّة True)، أو يتركه فارغًا ( القيمة المنطقيّة تكون False). لذا ففي WTForms يُمكننا استخدام مُربّع التأشير هذا باستيراد الصّنف BooleanField من المكتبة.

تعريف الحقل يكون كما يلي:

remember_me = BooleanField('Remember Me')

وطريقة العرض على صفحة HTML هي نفسها طريقة عرض الحقل السّلسلة النّصيّة القصيرة TextField:

{{form.remember_me}} {{form.remember_me.label}}
    {% if form.remember_me.errors %}
           <ul class=errors>
           {% for error in form.remember_me.errors %}
               <li>{{ error }}</li>
           {% endfor %}
           </ul>
    {% endif %}

لاحظ في أول سطر أعلاه بأنّنا نعرض المربّع أولا ثمّ اللصيقة ليكون الحقل أكثر تناسقًا.

عندما تُرسل بيانات النّموذج، فمُحتوى هذا الحقل يكون إمّا القيمة المنطقيّة True إذا ما أشّر المُستخدم على المُربّع، أو القيمة المنطقيّة False إن ترك المُربّع فارغًا.

لذا يُمكنك استخدام شرط منطقي في شيفرتك كما يلي:

if form.remember_me.data:
    remember_user()

مع افتراض أنّ الدّالة remember_user() تقوم بحفظ بيانات المُستخدم في جلسة طويلة الأمد ليتمكّن من الوصول إلى حسابه حتى ولو أغلق المُتصفّح.

إن كان المُستخدم قد أشّر على المُربّع فستكون قيمة form.remember_me.data صحيحة وبالتّالي ستُستدعى الدّالة remember_user() وأمّا إن لم يؤشّر المُستخدم على المُربّع فالقيمة ستكون False وبالتّالي سيتم تجاهل ذلك الجزء من الشّيفرة ولن تُستدعى الدّالة remember_user().

الوصول إلى نوعيّة الحقل

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

form.username.type #=> 'TextField'
form.password.type #=> 'PasswordField'
form.remember_me.type #=> 'BooleanField'

وكما تُلاحظ فالنّوع يكون اسم الصّنف على شكل سلسلة نصيّة، لذا تستطيع كتابة جملة شرطيّة كما يلي:

{% if field.type == "BooleanField" %}
    <div>{{ field }} {{ field.label }}</div>
{% endif %}

وهذا الأمر مُفيد كثيرًا إذا كنت تستعمل ميّزة الماكرو المُتواجدة في مُحرّك القوالب Jinja، وسنتطرّق إلى الأمر في الفقرة التّاليّة.

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

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

{% if form.username.errors %}
    {% for error in form.username.errors %}
         {{ error }}
    {% endfor %}
{% endif %}
{% if form.password.errors %}
    {% for error in form.password.errors %}
         {{ error }}
    {% endfor %}
{% endif %}

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

يُوفّر لنا مُحرّك القوالب Jinja ما يُسمّى بالماكرو Macro، ويُشبه كثيرا الدّوال العاديّة في لغة بايثون، والاختلاف الواضح أنّ الماكرو يستعمل بنية جملة مُحرّك القوالب، والتّالي الماكرو الخاص بعرض الحقول:

{% macro render_field(field) %}
  <dt>{{ field.label }}
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}

الماكرو يبدأ بالكلمة المفتاحيّة macro، ثمّ تُحدّد اسمًا له، وتُحدّد المُعاملات التّي يأخذها (في هذه الحالة يأخذ مُعاملًا واحدا يُمثّل الحقل)، بعدها نعرض اللصيقة الخاصّة بالحقل، ثمّ نعرض الحقل مع تمرير المُعاملات المفتاحية Keyword Arguments ونُنبّه مُحرّك القوالب بأنّ هذا الحقل آمن (لأنّ المُحتوى مكتوب بلغة HTML وjinja يقوم بمسح مُكوّنات HTML للتأمين من هجمات XSS)، بعدها يتم التّحقّق من أنّ هناك أخطاء من المُصادقين لعرضها، وستُلاحظ أنّ الماكرو ينتهي بالكلمة المفتاحيّة endmacro للدّلالة على أنّ ذلك الجزء من الشّيفرة قد انتهى.

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

عند استعمالك للماكرو أعلاه، تستطيع تحويل الشّيفرة الطّويلة أعلاه إلى سطرين فقط:

{{ render_field(form.username) }}
{{ render_field(form.password) }}

خاتمة

تعرّفنا في هذا الدّرس على بقيّة الحقول ومكوّنات HTML التّي تُوفّرها لنا مكتبة WTForms، وبالتّالي تستطيع الاعتماد كليّا على هذه المكتبة عوضًا عن كتابة نماذج تطبيقاتك مُباشرة بلغة HTML، وبالتّالي تأمين تطبيقاتك أكثر.

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


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

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

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



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

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

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

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


×
×
  • أضف...