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


مُقدّمة

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

main.png

المُصادقون Validators ميّزة من ميّزات مكتبة WTForms تُتيح لنا إمكانيّة التّحقق من مُدخلات المُستخدم والمُصادقة عليها حسب قانون مُعيّن (طول النّص، مجال عدد مُعيّن، صيغة بريد إلكتروني …)، وهناك أنواع مختلفة من المُصادقين، وقد تعرّفنا من قبل على المُصادق DataRequired الذي يتحقّق من أنّ المُدخل غير فارغ، ونُضيفه إلى كل حقل مطلوب ملؤه من قبل المُستخدم، ويرجع رسالة خطأ إذا ما لم تتم المُصادقة على المُدخل (في حالة المُصادق DataRequired فإنّ الرّسالة تُعرض إذا أرسل المُستخدم بيانات فارغة عبر النموذج).

وقد تعرّفنا كذلك على كيفيّة استخدام المُصادق، وذلك باستيراده من حزمة wtforms.validators ثمّ تمريره كعنصر من قائمة إلى المُعاملvalidators إلى الصّنف المسؤول عن الحقل عند تعريفه في البداية.
وإليك تذكيرا بسيطا لكيفيّة استيراد المُصادق وكيفيّة استعماله في حقل نصي بسيط:

from wtforms import TextField

from wtforms.validators import DataRequired

username = TextField('Username', validators=[DataRequired()]) 

الآن، أي إرسال للنّموذج مع هذا الحقل فارغا سيفشل، وستظهر رسالة خطأ للمُستخدم إذا ما عرضتها في صفحة HTML.

مُصادق البريد الإلكتروني Email

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

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

لاستعمال المُصادق، نقوم أولا باستيراده من حزمة wtforms.validators:

from flask_wtf import FlaskForm
from wtforms import TextField
from wtforms.validators import DataRequired, Email 



class SubscribeForm(FlaskForm):
    email = TextField(
      'Email', 
      validators=[
      Email(),
      DataRequired()]
       )

الحقل هنا هو حقل النّص القصير TextField.
لاحظ بأنّني أبقيت على المُصادق DataRequired للتّأكد من أنّ المُستخدم لا يُرسل النّموذج مع حقل فارغ، وأضفت المُصادق Email كعنصر آخر من القائمة validators.
إذا ما حاولت الآن إرسال النّموذج فارغا أو أدخلت بريدا إلكترونيا بشكل غير صحيح فستحصل على رسالة خطأ.

المُصادق EqualTo

قد ترغب في بعض الأحيان أن تتحقّق من أنّ بيانات مُدخل تُساوي بيانات حقل آخر، وأشهر تطبيق لهذه الفكرة هي حقل تأكيد كلمة المرور الذي يتواجد في مُعظم تطبيقات الويب، الفكرة أنّ المُستخدم قد يُدخل كلمة مروره عند التّسجيل بشكل خاطئ ويضطر إلى تعقيد عمليّة تسجيله أكثر من اللازم بمُحاولة إعادة استرجاع كلمة المرور وتغييرها، لذا فالحل أن نطلب من المُستخدم إدخال كلمة مروره مرّتين للتأكّد من أنّه لم يكتبها بشكل خاطئ، وبالطّبع فكلمة المرور التّي يُدخلها المُستخدم عند تسجيله يجب أن تتساوى مع المُدخل في حقل “تأكيد كلمة المرور”.
دور المُصادق واضح، إن لم تتساوى قيمتا الحقلين فرسالة الخطأ ستظهر.
لاستعمال المُصادق يجب عليك أولا استيراده وبعدها تُمرّره كما العادة إلى القائمة validators:

from flask_wtf import FlaskForm
from wtforms import TextField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo

class RegisterForm(FlaskForm):
    email = TextField(
      'Email', 
      validators=[
      Email(),
      DataRequired()]
       )
    password = PasswordField('Password', 
      validators=[DataRequired()])
    confirm  = PasswordField('Confirm your Password',
      validators=[DataRequired(), EqualTo('password')])

لاحظ بأنّنا نقوم بتمرير الحقل إلى المُصادق بتمرير اسم المُتغيّر الذي يمثّل الحقل (أي password في هذه الحالة) على شكل سلسلة نصيّة كما يلي:

EqualTo('password')

هكذا نُعلم المُصادق بأنّ قيمة هذا الحقل يجب أن تُساوي قيمة الحقل password.

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

المُصادق IPAddress

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

تذكّر فقط بأنّك ستحتاج إلى استيراد المُصادق قبل أن تتمكّن من استعماله للتّحقق من المُدخلات:

from wtforms.validators import IPAddress

يُستعمل المُعامل المُصادق على حقل نص قصير عادي كما يلي:

ip_address = TextField('IP address', 
    validators=[DataRequired(), IPAddress()])

هكذا لن تسمح مكتبة WTForms بمرور البيانات إلى حين توفير عنوان IP صالح.

عند استعمالك للمُصادق دون تمرير أي مُعامل، فسيتأكّد من أنّ المُدخل مُتوافق مع النّسخة الرّابعة لعناوين IP فقط وإن أردت أن تُقبل عناوين IPv6 كذلك فيُمكنك ذلك عبر تمرير القيمة True إلى المُعامل ipv6 كما يلي:

ip_address = TextField('Ip address', 
    validators=[DataRequired(), IPAddress(ipv6=True)])

المُصادق Length

إنّ من أهم الأسباب التّي قد تدفعك لاستخدام مكتبة للتّحقق من مُدخلات المُستخدم هي تفادي تخريب قاعدة بيانات التّطبيق ببيانات غير ضروريّة أو بيانات “تخريبيّة”، إذ لا يُمكن الثّقة بالمُستخدمين لأنّهم قد يرتكبون أخطاء عفويّة أو أنّ تطبيقك قد يتعرّض لهجمات بغرض التّخريب، وأهم طريقة لمنع المُستخدمين من استهلاك مساحة كبيرة في قاعدة البيانات هي بتحديد طول كل مُدخل، فاسم المُستخدم مثلا لا يجب أن يزيد عن عشرين حرفا، واسم قسم في الموقع لا يجب أن يكون أطول من اللازم وإلّا فسيُشوّه مظهر قائمة التّصفّح.
بالإضافة إلى تقليص طول بعض المُدخلات، فقد تحتاج في بعض الأحيان إلى عدم السّماح بمُدخلات قصيرة، فكلمة المرور مثلا لا يجب أن تكون أقصر من 6 أحرف وإلّا سيسهل اختراق حساب المُستخدم وقد تُفضّل كذلك أن لا يكون اسم المُستخدم أقصر من أربعة إلى خمسة أحرف للحفاظ على تنسيق صفحات الموقع التّي تظهر بها أسماء المُستخدمين.

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

يُمكنك استعمال المُصادق بعد استيراده كما يلي:

Length(min=MIN_VALUE, max=MAX_VALUE)

استبدل MIN_VALUE بعدد الأحرف الأدنى الذي يُمكن قبوله، واستبدل MAX_VALUE بعدد الأحرف الأقصى، وبالطّبع فإنّك تستطيع توفير قيمة دنيا فقط دون توفير قيمة قصوى، والعكس صحيح كذلك.
على سبيل المثال، لنستخدم المُصادق للتأكّد من أنّ اسم المُستخدم سيكون بين 3 إلى 25 حرفا:

username = TextField('Username', 
    validators=[DataRequired(),
        Length(min=3, max=25)]
    )

هكذا لن تُقبل أية قيمة إن كان طولها أقصر أو أطول ممّا حدّدناه.

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

username = TextField('Username', 
    validators=[DataRequired(),
        Length(max=25)]
    )

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

username = TextField('Username', 
    validators=[DataRequired(),
        Length(min=3)]
    )

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

المُصادق NumberRange

المُصادق Length يعمل مع السّلاسل النّصيّة فقط، ما يعني بأنّك تستطيع تطبيقه على حقل كلمة المرور، حقل النّصوص المُتعدّدة الأسطر Text Area أو أي حقل آخر يقبل قيما نصيّة، أمّا بالنّسبة لحقل الأعداد الصّحيحة IntegerField فهناك مُصادق آخر لتحديد مجال القيم العدديّة (قبول الأعداد الأكبر من 1 والأصغر من 255 على سبيل المثال).

لتحديد مجال الأعداد المقبول على حقل الأعداد الصّحيحة فسنستخدم المُصادق NumberRange، وطريقة استخدامه مُشابهة لطريقة استخدام المُصادق Length، إذ توفّر عددا للمُعامل min لتحديد القيمة الدّنيا وتمرّر عددا آخر للمُعامل max لتحديد أكبر عدد يُمكن قبوله.
والتّالي مثال بسيط على كيفيّة استيراده وتطبيقه على الحقل IntegerField:

from flask_wtf import FlaskForm
from wtforms import IntegerField
from wtforms.validators import NumberRange

class AgeForm(FlaskForm):
    age = IntegerField('Age', validators=[NumberRange(min=12, max=120)])

المثال واضح، أولا الاستيراد، ثمّ إنشاء صنف مع مُتغيّر ليُمثّل حقل الأعداد الصّحيحة، بعدها نمرّر المُصادق إلى القائمة validators ونمرّر قيمتين للمُعاملين min و max الأول لتحديد العدد 12 كأدنى قيمة والثّاني للتأكد من أنّ العدد المُدخل لا يتجاوز 120.

ومثلما هو عليه الحال مع المُصادق Length، فإنّك تستطيع ترك أحد المُعاملين ووضع حد واحد لقيم الحقل، كأن تسمح فقط بالأعداد الموجبة بتحديد العدد 0 كقيمة دنيا، أو أن تتحقّق من أنّ قيمة الحقل لا تتجاوز العدد 1000 بتحديده كقيمة للمُعامل max.

ما يلي مثال عن كيفيّة التّحقق من أنّ قيم الحقل أعداد موجبة:

NumberRange(min=0)

هكذا يُمكن إدخال أي عدد موجب دون حد لأقصى قيمة.

وللتّحقق من أنّ المُدخل لا يتجاوز العدد 100:

NumberRange(max=100)

سيسمح المُصادق الآن بمرور أي عدد ما دام لم يتجاوز المئة، ما يعني بأنّ الأعداد السّالبة مسموح بها كذلك.

المُصادق Optional

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

ما يلي مثال على كيفيّة استخدام المُصادق Optional:

from flask_wtf import FlaskForm
from wtforms import TextField
from wtforms.validators import Optional

class RegisterForm(FlaskForm):
    phone = TextField('Phone Number', validators=[Optional()])

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

المُصادق AnyOf

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

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

لاستخدام المُصادق نقوم أولا باستيراده من حزمة wtforms.validators ثمّ نُطبّقه على الحقل الذي نُريده.

from flask_wtf import FlaskForm
from wtforms import TextField
from wtforms.validators import AnyOf

class NameForm(FlaskForm):
    name = TextField('Name', validators=[AnyOf(['Ahmed', 'Khalid', 'Kamal'])])

بتطبيقنا لهذا المُصادق في المثال أعلاه، فسنتمكّن من التّحقق من أنّ قيمة المُدخل لن تخرج عن مجموعة القيم ‘Ahmed’ و’Khalid’ و’Kamal’، وهكذا سنتأكّد من أنّ قاعدة البيانات لا تحتوي سوى على هذه القيم وبالتّالي فإجراء عمليّات على البيانات الضّخمة مثل إحصائها أو ربطها ببيانات أخرى لن تؤدي إلى أية نتائج سلبيّة.

المُصادق NoneOf

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

ولاستثناء مجموعة من القيم فإنّنا نستخدم المُصادق NoneOf، وبما أنّ فكرته مُشابهة لفكرة المُصادق AnyOf فطريقة العمل هي نفسها، بحيث تُمرّر القيم التّي لا يجب أن توافق المُدخل إلى المُصادق كقائمة عند استدعائه، والتّالي مثال على كيفيّة استخدامه لاستثناء القيم ‘Ahmed’ و’Khalid’ و’Kamal’ من مُدخلات الحقل name:

from flask_wtf import FlaskForm
from wtforms import TextField
from wtforms.validators import NoneOf

class NameForm(FlaskForm):
    name = TextField('Country', validators=[NoneOf(['Ahmed', 'Khalid', 'Kamal'])])

بتطبيقك للمُصادِق ستتمكّن من التّحقق من أنّ المُدخل لا يوافق كلّا من الأسماء ‘Ahmed’ و’Khalid’ و’Kamal’ لذا فأي اسم آخر لا يندرج ضمنها سيُقبل وسيصل إلى الخادوم.

خاتمة

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





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


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



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن