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

إنشاء مرشحات Jinja خاصة بك لاستخدامها في تطبيقات Flask


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

مُقدّمة

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

main2.png

إنشاء مُرشّحات خاصّة بك

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

لكن قبل أن ننتقل إلى كيفيّة إنشاء مُرشّح جديد، يجب أن تُدرك بأنّ هناك أكثر من طريقة لإنشاء مُرشّح مع إطار العمل Flask ولأنّ طريقة إنشاء مُرشّح يُشبه إلى حد ما طريقة إنشاء مُوجّه ما، فمكان إنشاء المُرشّح مُهمّ، إذ يُحدّد المكان الذي تُنشئ فيه المُرشّح القوالبَ التّي تستطيع الوصول إلى هذا المُرشّح، وهذا لأنّ سياق التّطبيق يكون مُختلفا بين المُخطّطات blueprints والكائن app الرّئيسيّ إذ أنّ هذا الأخير يُعتبر عامًا (أي أنّ جميع القوالب تستطيع الوصول إلى المُرشّح) وعند استخدام مُخطّط لإنشاء مُرشّح، فالقوالب التّي ستتمكّن من الوصول إلى المُرشّح هي فقط القوالب المُرتبطة بالمُخطّطات ولن تتمكّن من استخدام المُرشّح في القوالب الأخرى.

بمعنى آخر، إن أنشأت مُرشّحا في المُخطّط الخاصّة بالمُستخدمين فلن تستطيع استخدامه سوى على قوالب HTML المتواجدة بمُجلّد templates في المسار project/users. والمُرشّحات التّي تُنشأ في المخطّط الخاصّ بالمقالات لن تستطيع استخدامها سوى مع القوالب الخاصّة بالمقالات، أمّا إن أردت إنشاء مُرشّحات للوصول إليها بشكل عام في كل التّطبيق بحيث تتمكّن جميع قوالب HTML من الوصول إلى المُرشّح واستخدامه فسيتوجّب عليك إنشاؤه باستعمال الكائن app الذي يتواجد في ملفّ __init__.py داخل مُجلّد المشروع project أي المسار project/__init__.py .

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

كيف يعمل المُرشّح

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


@app.template_filter('filter_name')
def filter_name_function(value): 
        return value

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

لاستخدام المُرشّح سيتوجّب عليك استخدام اسمه الذي خصّصته ما بين قوسين (أي الاسم filter_name في المثال أعلاه)، كما يلي:


{{ value | filter_name() }}

إن جرّبت المثال فستُلاحظ بأنّ القيمة value لا تتغيّر، وهذا لأنّ ما يُرجعه المُرشّح هو نفسه القيمة التّي يأخذها.

لتفهم أهميّة السّطر return، جرّب تغييره إلى ما يلي:


@app.template_filter('filter_name')
def filter_name_function(value): 
        return 'Hello World!'

الآن، المُرشّح لا يقوم سوى بمُهمّة واحدة، بحيث يقوم بتحويل أيّ قيمة يستقبلها إلى القيمة Hello World! ما يعني أنّك إذا استخدمته في أي مكان في قالب HTML فستتحوّل القيمة إلى Hello World!.

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

أمثلة على إنشاء مُرشّحات وكيفيّة استعمالها

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

كيفيّة إنشاء مُرشّح Jinja خاص بك، المثال الأول: مُرشّح ترجمة الكلمات

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

سيكون المُرشّح كالتّالي:


@app.template_filter('translate')
def translate_filter(value): 
    english_to_arabic = {'Hello': u'مرحبا',
                         'Flask': u'فلاسك',
                         'Python': u'بايثون',
                         'Javascript': u'جافاسكربت',
                         'Academy': u'أكاديميّة'
                         }
    arabic_word = english_to_arabic[value]
    return arabic_word

ستُلاحظ أولا بأنّنا سمّينا المُرشّح بالاسم translate وهذا هو الاسم الذي سنستعمله إذا ما أردنا تطبيق المُرشّح في قوالب HTML، أمّا الدّالة فاسمها translate_filter وكما قلت سابقا، فاسم الدّالة لا يهم، لكن من المُفضّل أن يشرح الاسم هدف الدّالة. بعدها أنشأنا القاموس english_to_arabic ليحتوي على بضعة كلمات إنجليزيّة ونظيراتها باللغة العربيّة كمثال بسيط، وبالطّبع، ففي مثال واقعي، سيتوجّب عليك الحصول على الكلمات وترجماتها من قاعدة بيانات وليس من قاموس عادي. في السّطر ما قبل الأخير، نُعرّف المُتغيّر arabic_word لتحمل الكلمة العربيّة التي نحصل عليها من القاموس باستعمال القيمة value التّي تُمرّر إلى المُرشّح كمفتاح. أمّا في السّطر الأخير فإنّنا نقوم بإرجاع مُحتوى المُتغيّر arabic_word.

المثال التّالي يوضّح كيفيّة استعمال المُرشّح الذي أنشأناه للتّو:


{{ 'Python' | translate }}

النّتيجة ستكون الكلمة "بايثون” لأنّها الكلمة التي تُعتبر قيمة للمفتاح Python في القاموس english_to_arabic.

المثال التّالي يشمل جميع الكلمات المُتواجدة في القاموس الصّغير الذي أنشأناه:


{% set words = ['Flask', 'Python', 'Hello', 'Javascript', 'Academy'] %}
{% for word in words %}

    <p> {{ word }}: {{ word | translate }} </p>

{% endfor %}


النّتيجة:


Flask: فلاسك
Python: بايثون
Hello: مرحبا
Javascript: جافاسكربت
Academy: أكاديميّة

كيفيّة إنشاء مُرشّح Jinja خاص بك، المثال الثّاني

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

إليك شيفرة المُرشّح:


@app.template_filter('list_len')
def list_length_filter(value): 
    joined_list = "".join(value)
    length = len(joined_list)
    return length

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

المثال التّالي يُوضّح كيفيّة المُرشّح list_len الذي أنشأناه مع قائمة باسم numbers:


{% set numbers = ['One', 'Two', 'Three'] %}


{{ numbers | list_len }}

النّتيجة ستكون العدد 11 وهو عدد أحرف الكلمات One، Two و Three مُجتمعة.

مثال آخر:


{% set words = ['Flask', 'Python', 'Hello', 'Javascript', 'Academy'] %}


{{ words | list_len }}

النّتيجة:


33

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

المثال التّالي يُوضّح كيفيّة الحصول على نفس النّتيجة باستعمال كل من join و length:


{{ words | join("") | length }}

كيفيّة إنشاء مُرشّح Jinja خاص بك، المثال الثّالث

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

يجب أن نكون قادرين على استعمال المُرشّح كما يلي:


image_link | image() | safe()

سيتوجّب علينا استخدام المُرشّح safe لأنّ نتيجة المُرشّح image ستكون عبارة عن شيفرة HTML، ولتمكين المُتصفّح من ترجمة هذه الشّيفرة، لا بد من استخدام المُرشّح safe.

** مُلاحظة: ** استخدام المُرشّح safe خطير كما تعلم، لذا فيجب عليك أن تتأكّد من أنّ رابط الصّورة سليم وآمن قبل استخدام المُرشّح image عليه، من طرق التّأكد من أنّ الرّابط سليم هو تفادي منح المُستخدمين إمكانيّة إضافة روابط للصوّر، فإن أدخل مُستخدم ما شيفرة HTML وشيفرة Javascript عوضا عن رابط صورة فقد يتعرّض تطبيقك لهجمة خبيثة قد تُمكّن المُهاجم من الوصول إلى قاعدة بيانات التّطبيق أو سرقة حسابات مُستخدميك، لذا تأكّد من أنّ رابط الصّورة آمن قبل استعمال المُرشّح safe.

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

لذا إن أردنا أن تكون الصّورة بحجم 200 px فسيتوجّب أن نكون قادرين على استخدام المُرشّح كما يلي:


image_link | image(width=200) | safe

بالإضافة إلى التّحكم بحجم الصّورة، سنُضيف كذلك إمكانيّة التّحكم بقيمة خاصيّة border-radius في لغة CSS لتحديد مدى استدارة حواف الصّورة.

مع هذه الإضافة، المثال التّالي سيُنتج صورة بحجم 100 px وقيمة 5 px للخاصيّة border-radius أي أنّها ستكون مُستديرة الحواف قليلا:


image_link | image(width=200, radius=5)

بعد أن تعرّفنا على فكرة المُرشّح، لننتقل الآن إلى إنشائه.

الشّيفرة التّالية تُمثّل شيفرة المُرشّح image مع إمكانيّة تحديد حجم الصّورة المعروضة وتحديد مدى استدارة حوافها:


@app.template_filter('image')
def image_filter(value, width=500, radius=0):

    css_style = "'width: {}px; border-radius: {}px'".format(width, radius)
    html_code = '''
                <img src={} style={} />
                '''.format(value, css_style)

    return html_code

هناك العديد من الأمور التّي قد لا تُفهم في هذا المُرشّح، لذا لا بد من شرح مُفصّل.

أولا، نقوم بإنشاء مُرشّح باسم image، الدّالة المسؤولة تأخذ ثلاثة مُعاملات، القيمة الأولى تُعطى للمُعامل الأول، وهي عبارة عن القيمة التّي يأخذها المُرشّح أثناء استدعائه (أي ما يسبق الرّمز |) والتّي ستكون رابط أو مسار الصّورة المرغوب عرضها، المُعامل width عبارة عن المُعامل الذي يُحدّد حجم الصّورة، إذا لم يتوفّر حجم مُحدّد أثناء استدعاء المُرشّح فالحجم الافتراضي هو 500. بالنّسبة للمُعامل radius فسيكون المُعامل الذي يُحدّد مدى استدارة حواف الصّورة، والقيمة الافتراضية هي 0 أي أنّ الصورة ستظهر بشكل عادي دون استدارة للحواف إذا لم تتوفّر قيمة أكبر من 0 للمُعامل radius أثناء استخدام المُرشّح على رابط الصّورة لعرضها.

نُعرّف المُتغيّر css_style داخل الدّالة ليحمل شيفرة لغة CSS لتعديل الصّورة حسب قيم كل من الحجم و مدى استدارة الحواف التّي نحصل عليها من المُتغيّرين width و radius. ستُلاحظ بأنّنا نستخدم التّابع format لدمج قيم المُتغيّرين داخل السّلسلة النّصيّة، بهذه الطّريقة ستكون القيمة الافتراضيّة للمُتغيّر css_style ما يلي:


'width: 500px; border-radius: 0px'

في السّطر التّالي، نُعرّف المُتغيّر html_code نقوم باستخدام خاصيّة تعدّد الأسطر في لغة بايثون عبر إحاطة شيفرة HTML بثلاثة علامات تنصيص ما يسمح لنا بتوزيع قيمة المُتغيّر على أكثر من سطر.

في شيفرة HTML، نقوم باستخدام الوسم <img> لعرض الصورة عبر تمرير رابط أو مسار الصّورة إلى الخاصيّة src، كما نقوم بتمرير شيفرة CSS التّي تتواجد داخل المُتغيّر css_style إلى الخاصيّة style لتنسيق الصّورة حسب الحجم ومدى استدارة الحواف بلغة CSS.

في الأخير نقوم بإرجاع شيفرة HTML المُتواجدة داخل المُتغيّر html_code والتّي ستكون مسؤولة عن عرض الصّورة.

اختبار مُرشّح عرض الصّور

بعد الانتهاء من إنشاء المُرشّح image لعرض الصّور، يُمكنك اختباره كما يلي:


image_link | image(width=100) | safe

مع التّأكد من أنّ المُتغيّر image_link عبارة عن رابط مُباشر للصورة.

يُمكنك مثلا تجربة المُرشّح مع صورة رمزيّة لأحد دروس إطار العمل Flask السّابقة عبر تجربة السّطر التّالي:


'https://academy.hsoub.com/uploads/monthly_2016_05/flask-introduction.png.b896f4a662940d3b348b5b95f5eac86a.png' | image(width=100) | safe

بعد تجربة المثال أعلاه، ستُلاحظ صورة درس "مدخل إلى تطوير تطبيقات الويب باستخدام إطار العمل Flask” بحجم 100px، ويُمكنك تغيير الحجم أو مدى استدارة الحواف كما تشاء.

التّالي مثال على كيفيّة عرض نفس الصّورة بحجم أكبر وحواف أكثر استدارة:


<center>
<h1> إنشاء مُرشّحات Jinja خاصّة بك </h1>
<br>

{{ 'https://academy.hsoub.com/uploads/monthly_2016_05/flask-introduction.png.b896f4a662940d3b348b5b95f5eac86a.png' | image(width=300, radius=20) | safe }}

</center>

نتيجة المثال أعلاه ستكون مُشابهة لما يلي:

IbTVwUU.png

خاتمة

تعرّفنا في هذا الدّرس والدّروس السّابقة على كيفيّة استخدام مُرشّحات Jinja وكيفيّة إنشاء مُرشّحات خاصّة بك، لذا ستتمكّن الآن من استعمال ما تعلّمته لتطوير تطبيقات أكثر تعقيدا لتُلبي احتياجاتك وتُطبّق أفكارك بسهولة وطرق أحسن من ذي قبل. في الدّرس المُقبل، سنُنهي سلسلة الدّروس المُخصّصة لمُحرّك القوالب 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.


×
×
  • أضف...