المحتوى عن 'python'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • مقالات عامة
  • التجارة الإلكترونية

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
    • Magento
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • برمجة أندرويد
  • لغة R
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات عامّة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
  • مقالات عامّة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجينتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 50 نتائج

  1. تمهيد توفر List Comprehensions طريقةً مختصرةً لإنشاء القوائم بناءً على قوائم موجودة مسبقًا. فعند استخدام list comprehensions فيمكن بناء القوائم باستخدام أيّ نوع من البيانات المتسلسلة التي يمكن الدوران على عناصرها عبر حلقات التكرار، بما في ذلك السلاسل النصية و tuples. من ناحية التركيب اللغوي، تحتوي list comprehensions على عنصر يمكن المرور عليه ضمن تعبيرٍ متبوعٍ بحلقة for. ويمكن أن يُتبَع ما سبق بتعابير for أو if إضافية، لذا سيساعدك الفهم العميق لحلقات for والعبارات الشرطية في التعامل مع list comprehensions. توفِّر list comprehensions طريقةً مختلفةً لإنشاء القوائم وغيرها من أنواع البيانات المتسلسلة. وعلى الرغم من إمكانية استخدام الطرائق الأخرى للدوران، مثل حلقات for، لإنشاء القوائم، لكن من المفضَّل استعمال list comprehensions لأنها تقلِّل عدد الأسطر الموجودة في برنامجك. List Comprehensions يمكن بناء list comprehensions في بايثون كالآتي: list_variable = [x for x in iterable] ستُسنَد القائمة، أو أي نوع من البيانات يمكن المرور على عناصره، إلى متغير. المتغيرات الإضافية –التي تُشير إلى عناصر موجودة ضمن نوع البيانات الذي يمكن المرور على عناصره– تُبنى حول عبارة for. والكلمة المحجوزة in تستعمل بنفس استعمالها في حلقات for وذلك لمرور على عناصر iterable. لننظر إلى مثالٍ يُنشِئ قائمةً مبنيةً على سلسلةٍ نصية: shark_letters = [letter for letter in 'shark'] print(shark_letters) أسندنا في المثال السابق قائمةً جديدةً إلى المتغير shark_letters، واستعملنا المتغير letter للإشارة إلى العناصر الموجودة ضمن السلسلة النصية 'shark'. استعملنا بعد ذلك الدالة print()‎ لكي نتأكد من القائمة الناتجة والمُسنَدة إلى المتغير shark_letters، وحصلنا على الناتج الآتي: ['s', 'h', 'a', 'r', 'k'] القائمة التي أنشأناها باستخدام list comprehensions تتألف من العناصر التي تكوِّن السلسلة النصية 'shark'، وهي كل حرف في الكلمة shark. يمكن إعادة كتابة تعابير list comprehensions كحلقات for، لكن لاحظ أنَّك لا تستطيع إعادة كتابة كل حلقة for بصيغة list comprehensions. لنعد كتابة المثال السابق الذي أنشأنا فيه القائمة shark_letters باستخدام حلقة for، وهذا سيساعدنا في فهم كيف تعمل list comprehensions عملها: shark_letters = [] for letter in 'shark': shark_letters.append(letter) print(shark_letters) عند إنشائنا للقائمة عبر استخدام الحلقة for، فيجب تهيئة المتغير الذي سنُسنِد العناصر إليه كقائمة فارغة، وهذا ما فعلناه في أوّل سطر من الشيفرة السابقة. ثم بدأت حلقة for بالدوران على عناصر السلسلة النصية 'shark' مستعملةً المتغير letter للإشارة إلى قيمة العنصر الحالي. ومن ثم أضفنا كل عنصر في السلسلة النصية إلى القائمة ضمن حلقة for وذلك باستخدام الدالة list.append(x). الناتج من حلقة for السابقة يماثل ناتج list comprehension في المثال أعلاه: ['s', 'h', 'a', 'r', 'k'] الخلاصة: يمكن إعادة كتابة List comprehensions كحلقات for، لكن بعض حلقات for يمكن إعادة كتابتها لتصبح List comprehensions لتقليل كمية الشيفرات المكتوبة. استخدام التعابير الشرطية مع List Comprehensions يمكن استخدام التعابير الشرطية في list comprehension لتعديل القوائم أو أنواع البيانات المتسلسلة الأخرى عند إنشاء قوائم جديدة. لننظر إلى مثالٍ عن استخدام العبارة الشرطية if في تعبير list comprehension: fish_tuple = ('blowfish', 'clownfish', 'catfish', 'octopus') fish_list = [fish for fish in fish_tuple if fish != 'octopus'] print(fish_list) استعملنا المتغير fish_tuple الذي من نوع البيانات tuple كأساس للقائمة الجديدة التي سنُنشِئها التي تسمى fish_list. استعملنا for و in كما في القسم السابق، لكننا أضفنا هنا العبارة الشرطية if. ستؤدي العبارة الشرطية if إلى إضافة العناصر غير المساوية للسلسلة النصية 'octopus'، لذا ستحتوي القائمة الجديدة على العناصر الموجودة في بنية tuple والتي لا تُطابِق الكلمة 'octopus'. عند تشغيل البرنامج السابق فسنلاحظ أنَّ القائمة fish_list تحتوي على نفس العناصر التي كانت موجودة في fish_tuple لكن مع حذف العنصر 'octopus': ['blowfish', 'clownfish', 'catfish'] أي أصبحت القائمة الجديدة تحتوي على بنية tuple الأصلية لكن ما عدا السلسلة النصية التي استثنيناها عبر التعبير الشرطي. سنُنشِئ مثالًا آخر يستعمل المعاملات الرياضية والأرقام الصحيحة والدالة range()‎: number_list = [x ** 2 for x in range(10) if x % 2 == 0] print(number_list) القائمة التي ستُنشَأ باسم number_list ستحتوي على مربع جميع القيم الموجودة من المجال 0 إلى 9 لكن إذا كان الرقم قابلًا للقسمة على 2. وستبدو المخرجات كالآتية: [0, 4, 16, 36, 64] دعنا نُفصِّل ما الذي يفعله تعبير list comprehension السابق، ودعنا نفكِّر بالذي سيظهر إذا استعملنا التعبير x for x in range(10) فقط. يجب أن يبدو برنامجنا الصغير كالآتي: number_list = [x for x in range(10)] print(number_list) الناتج: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] لنضف العبارة الشرطية الآن: number_list = [x for x in range(10) if x % 2 == 0] print(number_list) الناتج: [0, 2, 4, 6, 8] أدّت العبارة الشرطية if إلى قبول العناصر القابلة للقسمة على 2 فقط وإضافتها إلى القائمة، مما يؤدي إلى حذف جميع الأرقام الفردية. يمكننا الآن استخدام معامل رياضي لتربيع قيمة المتغير x: number_list = [x ** 2 for x in range(10) if x % 2 == 0] print(number_list) أي ستُربَّع قيم القائمة السابقة ‎[0, 2, 4, 6, 8] وسيُخرَج الناتج الآتي: [0, 4, 16, 36, 64] يمكننا أيضًا استعمال ما يشبه عبارات if المتشعبة في تعابير list comprehension: number_list = [x for x in range(100) if x % 3 == 0 if x % 5 == 0] print(number_list) سيتم التحقق أولًا أنَّ المتغير x قابل للقسمة على الرقم 3، ثم سنتحقق إن كان المتغير x قابل للقسمة على الرقم 5، وإذا حقَّق المتغير x الشرطين السابقين فسيُضاف إلى القائمة، وسيُظهَر في الناتج: [0, 15, 30, 45, 60, 75, 90] الخلاصة: يمكن استخدام عبارات if الشرطية لتحديد ما هي العناصر التي نريد إضافتها إلى القائمة الجديدة. حلقات التكرار المتشعبة في تعابير List Comprehension يمكن استعمال حلقات التكرار المتشعبة لإجراء عدِّة عمليات دوران متداخلة في برامجنا. سننظر في هذا القسم إلى حلقة for متشعبة وسنحاول تحويلها إلى تعبير list comprehension. هذه الشيفرة ستُنشِئ قائمةً جديدةً بالدوران على قائمتين وبإجراء عمليات رياضية عليها: my_list = [] for x in [20, 40, 60]: for y in [2, 4, 6]: my_list.append(x * y) print(my_list) سنحصل على الناتج الآتي عند تشغيل البرنامج: [40, 80, 120, 80, 160, 240, 120, 240, 360] الشيفرة السابقة تضرب العناصر الموجودة في أوّل قائمة بالعناصر الموجودة في ثاني قائمة في كل دورة. لتحويل ما سبق إلى تعبير list comprehension، وذلك باختصار السطرين الموجودين في الشيفرة السابقة وتحويلهما إلى سطرٍ وحيدٍ، الذي يبدأ بإجراء العملية x*y، ثم ستلي هذه العملية حلقة for الخارجية، ثم يليها حلقة for الداخلية؛ وسنضيف تعبير print()‎ للتأكد أنَّ ناتج القائمة الجديدة يُطابِق ناتج البرنامج الذي فيه حلقتين متداخلتين: my_list = [x * y for x in [20, 40, 60] for y in [2, 4, 6]] print(my_list) الناتج: [40, 80, 120, 80, 160, 240, 120, 240, 360] أدى استعمال تعبير list comprehension في المثال السابق إلى تبسيط حلقتَي for لتصبحا سطرًا وحيدًا، لكن مع إنشاء نفس القائمة والتي ستُسنَد إلى المتغير my_list. توفِّر لنا تعابير list comprehension طريقةً بسيطةً لإنشاء القوائم، مما يسمح لنا باختصار عدِّة أسطر إلى سطرٍ وحيد. لكن من المهم أن تبقي في ذهنك أنَّ سهولة قراءة الشيفرة لها الأولوية دومًا، لذا إذا أصبحتَ تعابير list comprehension طويلةً جدًا ومعقدة، فمن الأفضل حينها تحويلها إلى حلقات تكرار عادية. الخلاصة تسمح تعابير list comprehension لنا بتحويل قائمة أو أي نوع من البيانات المتسلسلة إلى سلسلةٍ جديدة، ولها شكلٌ بسيطٌ يُقلِّل عدد الأسطر التي نكتبها. تتبع تعابير list comprehension شكلًا رياضيًا معيّنًا، لذا قد يجدها المبرمجون أولو الخلفية الرياضية سهلة الفهم. وصحيحٌ أنَّ تعابير list comprehension تختصر الشيفرةـ لكن من المهم جعل سهولة قراءة الشيفرة من أولوياتنا، وحاول تجنّب الأسطر الطويلة لتسهيل قراءة الشيفرة. ترجمة –وبتصرّف– للمقال Understanding List Comprehensions in Python 3لصاحبته Lisa Tagliaferri
  2. يسمح لنا استخدام حلقات التكرار في برمجة الحاسوب بأتمتة وتكرار المهام المتشابهة مرّاتٍ عدِّة. وسنشرح في هذا الدرس كيفية استخدام حلقة for في بايثون. حلقة for تؤدي إلى تكرار تنفيذ جزء من الشيفرات بناءً على عدّاد أو على متغير، وهذا يعني أنَّ حلقات for تستعمل عندما يكون عدد مرات تنفيذ حلقة التكرار معلومًا قبل الدخول في الحلقة، وذلك على النقيض من حلقات while المبنية على شرط. حلقات for تُبنى حلقات for في بايثون كما يلي: for [iterating variable] in [sequence]: [do something] ستُنفَّذ الشيفرات الموجودة داخل حلقة التكرار عدِّة مرات إلى أن تنتهي الحلقة. لننظر إلى كيفية مرور الحلقة for على مجالٍ من القيم: for i in range(0,5): print(i) سيُخرِج البرنامج السابق عند تشغيله الناتج الآتي: 0 1 2 3 4 ضبطنا المتغير i في حلقة for ليحتوي على القيمة التي ستُنفَّذ عليها حلقة التكرار، وكان مجال القيم التي ستُسنَد إلى هذا المتغير من 0 إلى 5. ثم طبعًا قيمة المتغير في كل دوران لحلقة التكرار، لكن أبقِ في ذهنك أنَّنا نميل إلى بدء العد من الرقم 0 في البرمجة، وعلى الرغم من عرض خمسة أرقام، لكنها تبدأ بالرقم 0 وتنتهي بالرقم 4. من الشائع أن ترى استخدامًا لحلقة for عندما تحتاج إلى تكرار كتلة معيّنة من الشيفرات لعددٍ من المرات. استخدام حلقات التكرار مع الدالة range()‎ إحدى أنواع السلاسل غير القابلة للتعديل في بايثون هي تلك الناتجة من الدالة range()‎، وتستخدم الدالة range()‎ في حلقات التكرار للتحكم بعدد مرات تكرار الحلقة. عند التعامل مع الدالة range()‎ عليك أن تمرر معاملًا رقميًا أو معاملين أو ثلاثة معاملات: start يشير إلى القيم العددية الصيحية التي ستبدأ بها السلسلة، وإذا لم تُمرَّر قيمة لهذا المعامل فستبدأ السلسلة من 0 stop هذا المعامل مطلوب دومًا وهو القيمة العددية الصحيحة التي تمثل نهاية السلسلة العددية لكن دون تضمينها step هي مقدار الخطوة، أي عدد الأرقام التي يجب زيادتها (أو إنقاصها إن كنّا نتعامل مع أرقام سالبة) في الدورة القادمة، وقيمة المعامل step تساوي 1 في حال لم تُحدَّد له قيمة لننظر إلى بعض الأمثلة التي نُمرِّر فيها مختلف المعاملات إلى الدالة range()‎. لنبدأ بتمرير المعامل stop فقط، أي أنَّ السلسلة الآتية من الشكل range(stop): for i in range(6): print(i) كانت قيمة المعامل stop في المثال السابق مساويةً للرقم 6، لذا ستمر حلقة التكرار من بداية المجال 0 إلى نهايته 6 (باستثناء الرقم 6 كما ذكرنا أعلاه): 0 1 2 3 4 5 المثال الآتي من الشكل range(start ,stop) الذي تُمرَّر قيم بدء السلسلة ونهايتها: for i in range(20,25): print(i) المجال –في المثال السابق– يتراوح بين 20 (بما فيها الرقم 20) إلى 25 (باستثناء الرقم 25)، لذا سيبدو الناتج كما يلي: 20 21 22 23 24 الوسيط step الخاص بالدالة range()‎ شبيه بمعامل الخطوة الذي نستعمله عند تقسيم [السلاسل النصية](آلية فهرسة السلاسل النصية وطريقة تقسيمها في بايثون 3) لأنه يستعمل لتجاوز بعض القيم ضمن السلسلة. يأتي المعامل step في آخر قائمة المعاملات التي تقبلها الدالة range()‎ وذلك بالشكل الآتي range(start, stop, step). لنستعمل المعامل step مع قيمة موجبة: for i in range(0,15,3): print(i) سيؤدي المثال السابق إلى إنشاء سلسلة من الأرقام التي تبدأ من 0 وتنتهي عند 15 لكن قيمة المعامل step هي 3، لذا سيتم تخطي رقمين في كل دورة، أي سيكون الناتج كالآتي: 0 3 6 9 12 يمكننا أيضًا استخدام قيمة سالبة للمعامل step للدوران إلى الخلف، لكن علينا تعديل قيم start و stop بما يتوافق مع ذلك: for i in range(100,0,-10): print(i) قيمة المعامل start في المثال السابق هي 100، وكانت قيمة المعامل stop هي 0، والخطوة هي ‎-10، لذا ستبدأ السلسلة من الرقم 100 وستنتهي عند الرقم 0، وسيكون التناقص بمقدار 10 في كل دورة، ويمكننا ملاحظة ذلك في الناتج الآتي: 100 90 80 70 60 50 40 30 20 10 الخلاصة: عندما نبرمج باستخدام لغة بايثون، فسنجد أننا نستفيد كثيرًا من السلاسل الرقمية التي تنتجها الدالة range()‎. استخدام حلقة for مع أنواع البيانات المتسلسلة يمكن الاستفادة من القوائم (من النوع list) وغيرها من أنواع البيانات المتسلسلة واستعمالها كمعاملات لحلقات for، فبدلًا من الدوران باستخدام الدالة range()‎ فيمكننا تعريف قائمة ثم الدوران على عناصرها. سنُسنِد في المثال الآتي قائمةً إلى متغير، ثم سنستخدم حلقة for للدوران على عناصر القائمة: sharks = ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem'] for shark in sharks: print(shark) في هذه الحالة، قمنا بطباعة كل عنصر موجود في القائمة؛ وصحيحٌ أننا استعملنا الكلمة shark كاسم للمتغير، لكن يمكنك استعمال أي اسم صحيح آخر ترغب به، وستحصل على نفس النتيجة: hammerhead great white dogfish frilled bullhead requiem الناتج السابق يُظهِر دوران الحلقة for على جميع عناصر القائمة مع طباعة كل عنصر في سطرٍ منفصل. يشيع استخدام القوائم والأنواع الأخرى من البيانات المتسلسلة مثل السلاسل النصية وبنى tuple مع حلقات التكرار لسهولة الدوران على عناصرها. يمكنك دمج هذه الأنواع من البيانات مع الدالة range()‎ لإضافة عناصر إلى قائمة، مثلًا: sharks = ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem'] for item in range(len(sharks)): sharks.append('shark') print(sharks) الناتج: ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark'] أضفنا هنا السلسلة النصية 'shark' خمس مرات (وهو نفس طول القائمة sharks الأصلي) إلى القائمة sharks. يمكننا استخدام حلقة for لبناء قائمة جديدة: integers = [] for i in range(10): integers.append(i) print(integers) هيّئنا في المثال السابق قائمةً فارغةً باسم integers لكن حلقة التكرار for ملأت القائمة لتصبح كما يلي: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] وبشكلٍ شبيهٍ بما سبق، يمكننا الدوران على السلاسل النصية: sammy = 'Sammy' for letter in sammy: print(letter) الناتج: S a m m y يمكن الدوران على بنى tuple كما هو الحال في القوائم والسلاسل النصية. عند المرور على عناصر نوع البيانات dictionary، فمن المهم أن تبقي بذهنك البنية الخاصة به (key:value) لكي تضمن أنَّك تستدعي العنصر الصحيح من المتغير. هذا مثالٌ بسيطٌ نعرض فيه المفتاح (key) والقيمة (value): sammy_shark = {'name': 'Sammy', 'animal': 'shark', 'color': 'blue', 'location': 'ocean'} for key in sammy_shark: print(key + ': ' + sammy_shark[key]) الناتج: name: Sammy animal: shark location: ocean color: blue عند استخدام متغيرات من النوع dictionary مع حلقات for فيكون المتغير المرتبط بحلقة التكرار متعلقًا بمفتاح القيم، وعلينا استخدام التعبير dictionary_variable[iterating_variable] للوصول إلى القيمة الموافقة للمفتاح. ففي المثال السابق كان المتغير المرتبط بحلقة التكرار باسم key وهو يُمثِّل المفاتيح، واستعملنا التعبير sammy_shark[key] للوصول إلى القيمة المرتبطة بذاك المفتاح. الخلاصة: تُستعمَل حلقات التكرار عادةً للدوران على عناصر البيانات المتسلسلة وتعديلها. حلقات for المتشعّبة يمكن تشعّب حلقات التكرار في بايثون، كما هو الحال في بقية لغات البرمجة. حلقة التكرار المتشعبة هي الحلقة الموجودة ضمن حلقة تكرار أخرى، وهي شبيهة بعبارات if المتشعّبة. تُبنى حلقات التكرار المتشعبة كما يلي: for [first iterating variable] in [outer loop]: # Outer loop [do something] # Optional for [second iterating variable] in [nested loop]: # Nested loop [do something] يبدأ البرنامج بتنفيذ حلقة التكرار الخارجية، ويُنفَّذ أوّل دوران فيها، وأوّل دوران سيؤدي إلى الدخول إلى حلقة التكرار الداخلية، مما يؤدي إلى تنفيذها إلى أن تنتهي تمامًا. ثم سيعود تنفيذ البرنامج إلى بداية حلقة التكرار الخارجية، ويبدأ بتنفيذ الدوران الثاني، ثم سيصل التنفيذ إلى حلقة التكرار الداخلية، وستُنفَّذ حلقة التكرار الداخلية بالكامل، ثم سيعود التنفيذ إلى بداية حلقة التكرار الخارجية، وهلّم جرًا إلى أن ينتهي تنفيذ حلقة التكرار الخارجية أو إيقاف حلقة التكرار عبر استخدام [التعبير break](كيفية استخدام تعابير break و continue و pass عند التعامل مع حلقات التكرار في بايثون 3) أو غيره من التعابير. لنُنشِئ مثالًا يستعمل حلقة forمتشعبة لكي نفهم كيف تعمل بدقة. حيث ستمر حلقة التكرار الخارجية في المثال الآتي على قائمة من الأرقام اسمها num_list، أما حلقة التكرار الداخلية فستمر على قائمة من السلاسل النصية اسمها alpha_list: num_list = [1, 2, 3] alpha_list = ['a', 'b', 'c'] for number in num_list: print(number) for letter in alpha_list: print(letter) سيظهر الناتج الآتي عند تشغيل البرنامج: 1 a b c 2 a b c 3 a b c يُظهِر الناتج السابق أنَّ البرنامج أكمل أوّل دوران على عناصر حلقة التكرار الخارجية بطباعة الرقم 1، ومن ثم بدأ تنفيذ حلقة التكرار الدخلية مما يطبع الأحرف a و b و c على التوالي. وبعد انتهاء تنفيذ حلقة التكرار الداخلية، فعاد البرنامج إلى بداية حلقة التكرار الخارجية طابعًا الرقم 2، ثم بدأ تنفيذ حلقة التكرار الداخلية (مما يؤدي إلى إظهار a و b و c مجددًا). وهكذا. يمكن الاستفادة من حلقات for المتشعبة عند المرور على عناصر قوائم تتألف من قوائم. فلو استعملنا حلقة تكرار وحيدة لعرض عناصر قائمة تتألف من عناصر تحتوي على قوائم، فستُعرَض قيم القوائم الداخلية: list_of_lists = [['hammerhead', 'great white', 'dogfish'],[0, 1, 2],[9.9, 8.8, 7.7]] for list in list_of_lists: print(list) الناتج: ['hammerhead', 'great white', 'dogfish'] [0, 1, 2] [9.9, 8.8, 7.7] وفي حال أردنا الوصول إلى العناصر الموجودة في القوائم الداخلية، فيمكننا استعمال حلقة for متشعبة: list_of_lists = [['hammerhead', 'great white', 'dogfish'],[0, 1, 2],[9.9, 8.8, 7.7]] for list in list_of_lists: for item in list: print(item) الناتج: hammerhead great white dogfish 0 1 2 9.9 8.8 7.7 الخلاصة: نستطيع الاستفادة من حلقات for المتشعبة عندما نريد الدوران على عناصر محتوى في قوائم. الخلاصة رأينا في هذا الدرس كيف تعمل حلقة التكرار for في لغة بايثون، وكيف نستطيع إنشاءها واستعمالها. حيث تستمر حلقة for بتنفيذ مجموعة من الشيفرات لعددٍ مُحدِّدٍ من المرات. ترجمة –وبتصرّف– للمقال How To Construct For Loops in Python 3لصاحبته Lisa Tagliaferri
  3. تمهيد يسمح لك استخدام حلقات for أو while في بايثون بأتمتة وتكرار المهام بطريقة فعّالة. لكن في بعض الأحيان، قد يتدخل عامل خارجي في طريقة تشغيل برنامجك، وعندما يحدث ذلك، فربما تريد من برنامجك الخروج تمامًا من حلقة التكرار، أو تجاوز جزء من الحلقة قبل إكمال تنفيذها، أو تجاهل هذا العامل الخارجي تمامًا. لذا يمكنك فعل ما سبق باستخدام تعابير break و continue و pass. التعبير break يوفِّر لك التعبير break القدرة على الخروج من حلقة التكرار عند حدوث عامل خارجي. حيث عليك وضع التعبير break في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. ألقِ نظرةً إلى أحد الأمثلة الذي يستعمل التعبير break داخل حلقة for: number = 0 for number in range(10): number = number + 1 if number == 5: break # break here print('Number is ' + str(number)) print('Out of loop') هذا برنامجٌ صغيرٌ، هيّأنا في بدايته المتغير number بجعله يساوي الصفر، ثم بنينا حلقة تكرار for التي تعمل لطالما كانت قيمة المتغير number أصغر من 10. ثم قمنا بزيادة قيمة المتغير number داخل حلقة for بمقدار 1 في كل تكرار، وذلك في السطر number = number + 1. ثم كانت هنالك عبارة if التي تختبر إن كان المتغير number مساوٍ للرقم 5، وعند حدوث ذلك فسيُنفَّذ التعبير break للخروج من الحلقة. وتوجد داخل حلقة التكرار الدالة print()‎ التي تُنفَّذ في كل تكرار إلى أن نخرج من الحلقة عبر التعبير break، وذلك لأنَّها موجودة بعد التعبير break. لكي نتأكد أننا خرجنا من الحلقة، فوضعنا عبارة print()‎ أخيرة موجودة خارج حلقة for. سنرى الناتج الآتي عند تنفيذ البرنامج: Number is 1 Number is 2 Number is 3 Number is 4 Out of loop الناتج السابق يُظهِر أنَّه بمجرد أن أصبح العدد الصحيح number مساويًا للرقم 5، فسينتهي تنفيذ حلقة التكرار عبر التعبير break. الخلاصة: التعبير break يؤدي إلى الخروج من حلقة التكرار. التعبير continue التعبير continue يسمح لنا بتخطي جزء من حلقة التكرار عند حدوث عامل خارجي، لكن إكمال بقية الحلقة إلى نهايتها. بعبارةٍ أخرى: سينتقل تنفيذ البرنامج إلى أوّل حلقة التكرار عند تنفيذ التعبير continue. يجب وضع التعبير continue في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أعلاه، لكننا سنستخدم التعبير continue بدلًا من break: number = 0 for number in range(10): number = number + 1 if number == 5: continue # continue here print('Number is ' + str(number)) print('Out of loop') الفرق بين استخدام التعبير continue بدلًا من break هو إكمال تنفيذ الشيفرة بغض النظر عن التوقف الذي حدث عندما كانت قيمة المتغير number مساويةً إلى الرقم 5. لننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop نلاحظ أنَّ السطر الذي يجب أن يحتوي على Number is 5 ليس موجودًا في المخرجات، لكن سيُكمَل تنفيذ حلقة التكرار بعد هذه المرحلة مما يطبع الأرقام من 6 إلى 10 قبل إنهاء تنفيذ الحلقة. يمكنك استخدام التعبير continue لتفادي استخدام تعابير شرطية معقدة ومتشعّبة، أو لتحسين أداء البرنامج عن طريق تجاهل الحالات التي ستُرفَض نتائجها. الخلاصة: التعبير continue سيؤدي إلى جعل البرنامج يتجاهل تنفيذ حلقة التكرار عند تحقيق شرط معين، لكن بعدئذٍ سيُكمِل تنفيذ الحلقة كالمعتاد. التعبير pass التعبير pass يسمح لنا بالتعامل مع أحد الشروط دون إيقاف عمل حلقة التكرار بأي شكل، أي ستُنفَّذ جميع التعابير البرمجية الموجودة في حلقة التكرار ما لم تستعمل تعابير مثل break أو continue فيها. وكما هو الحال مع التعابير السابقة، يجب وضع التعبير pass في الشيفرة التي ستُنفَّذ في كل تكرار للحلقة، ويوضع عادةً ضمن تعبير if. سنستخدم نفس البرنامج الذي استعملناها لشرح التعبير break أو continue أعلاه، لكننا سنستخدم التعبير pass هذه المرة: number = 0 for number in range(10): number = number + 1 if number == 5: pass # pass here print('Number is ' + str(number)) print('Out of loop') التعبير pass الذي يقع بعد العبارة الشرطية if يخبر البرنامج أنَّ عليه إكمال تنفيذ الحلقة وتجاهل مساواة المتغير number للرقم 5. لنشغِّل البرنامج ولننظر إلى الناتج: Number is 1 Number is 2 Number is 3 Number is 4 Number is 5 Number is 6 Number is 7 Number is 8 Number is 9 Number is 10 Out of loop لاحظنا عند استخدامنا للتعبير pass في هذا البرنامج أنَّ البرنامج يعمل كما لو أننا لم نضع عبارة شرطية داخل حلقة التكرار؛ حيث يخبر التعبير pass البرنامج أن يكمل التنفيذ كما لو أنَّ الشرط لم يتحقق. يمكن أن تستفيد من التعبير pass عندما تكتب برنامجك لأوّل مرة أثناء تفكيرك بحلّ مشكلة ما عبر خوارزمية، لكن قبل أن تضع التفاصيل التقنية له. الخلاصة تسمح لك التعابير break و continue و pass باستعمال حلقات for و while بطريقةٍ أكثر كفاءة. ترجمة –وبتصرّف– للمقال How To Use Break, Continue, and Pass Statements when Working with Loops in Python 3لصاحبته Lisa Tagliaferri
  4. تمهيد نستفيد من البرامج الحاسوبية خيرَ استفادة في أتمتة المهام وإجراء المهام التكرارية لكيلا نحتاج إلى القيام بها يدويًا، وإحدى طرائق تكرار المهام المتشابهة هي استخدام حلقات التكرار، وسنشرح في درسنا هذا حلقة تكرار while. حلفة تكرار while تؤدي إلى تكرار تنفيذ قسم من الشيفرة بناءً على متغير منطقي (boolean)، وسيستمر تنفيذ هذه الشيفرة لطالما كانت نتيجة التعبير المستعمل معها تساوي true. يمكنك أن تتخيل أنَّ حلقة while هي عبارة شريطة تكرارية، فبعد انتهاء تنفيذ العبارة الشرطية if فيُستَكمَل تنفيذ بقية البرنامج، لكن مع حلقة while فسيعود تنفيذ البرنامج إلى بداية الحلقة بعد انتهاء تنفيذها إلى أن يصبح الشرط مساويًا للقيمة false. وعلى النقيض من حلقات for التي تُنفَّذ عدد معيّن من المرات، فسيستمر تنفيذ حلقات while اعتمادًا على شرطٍ معيّن، لذا لن تحتاج إلى عدد مرات تنفيذ الحلقة قبل إنشائها. حلقة while الشكل العام لحلقات while في لغة بايثون كالآتي: while [a condition is True]: [do something] سيستمر تنفيذ التعليمات البرمجية الموجودة داخل الحلقة إلى أن يصبح الشرط false. لنُنشِئ برنامجًا صغيرًا فيه حلقة while، ففي هذه البرنامج سنطلب من المستخدم إدخال كلمة مرور. وهنالك خياران أمام حلقة التكرار: - إما أن تكون كلمة المرور صحيحة، فعندها سينتهي تنفيذ حلقة while. - أو أن تكون كلمة المرور غير صحيحة، فعندها سيستمر تنفيذ حلقة التكرار. لنُنشِئ ملفًا باسم password.py في محررنا النصي المفضَّل، ولنبدأ بتهيئة المتغير paasword بإسناد سلسلة نصية فارغة إليه: password = '' نستخدم المتغير السابق للحصول على مدخلات المستخدم داخل حلقة التكرار while. علينا بعد ذلك إنشاء حلقة while مع تحديد ما هو الشرط الذي يجب تحقيقه: password = '' while password != 'password': أتبَعنا –في المثال السابق– الكلمة المحجوزة while بالمتغير password، ثم سنتحقق إذا كانت قيمة المتغير password تساوي السلسلة النصية 'password' (لا تنسَ أنَّ قيمة المتغير سنحصل عليها من مدخلات المستخدم)، يمكنك أن تختار أي سلسلة نصية تشاء لمقارنة مدخلات المستخدم بها. هذا يعني أنَّه لو أدخل المستخدم السلسلة النصية password فستتوقف حلقة التكرار وسيُكمَل تنفيذ البرنامج وستُنفَّذ أيّة شيفرات خارج الحلقة، لكن إذا أدخل المستخدم أيّة سلسلة نصية لا تساوي password فسيُكمَل تنفيذ الحلقة. علينا بعد ذلك إضافة الشيفرة المسؤولة عمّا يحدث داخل حلقة while: password = '' while password != 'password': print('What is the password?') password = input() نفَّذ البرنامج عبارة print داخل حلقة while والتي تسأل المستخدم عن كلمة مروره، ثم أسندنا قيمة مدخلات المستخدم (التي حصلنا عليها عبر الدالة input()‎) إلى المتغير password. سيتحقق البرنامج إذا كانت قيمة المتغير password تساوي السلسلة النصية 'password'، وإذا تحقق ذلك فسينتهي تنفيذ حلقة while. لنضف سطرًا آخر إلى البرنامج لنعرف ماذا يحدث إن أصبحت قيمة الشرط مساويةً إلى false: password = '' while password != 'password': print('What is the password?') password = input() print('Yes, the password is ' + password + '. You may enter.') لاحظ أنَّ آخر عبارة print()‎ موجودة خارج حلقة while، لذا عندما يُدخِل المستخدم الكلمة password عند سؤاله عن كلمة مروره، فستُطبَع آخر جملة والتي تقع خارج حلقة التكرار. لكن ماذا يحدث لو لم يدخل المستخدم الكلمة password قط؟ حيث لن يستمر تنفيذ البرنامج ولن يروا آخر عبارة print()‎ وسيستمر تنفيذ حلقة التكرار إلى ما لا نهاية! يستمر تنفيذ حلقة التكرار إلى ما لا نهاية إذا بقي تنفيذ البرنامج داخل حلقة تكرار دون الخروج منها. وإذا أردتَ الخروج من حلقة تكرار نهائية، فاضغط Ctrl+C في سطر الأوامر. احفظ البرنامج ثم شغِّله: python password.py سيُطلَب منك إدخال كلمة المرور، ويمكنك تجربة ما تشاء من الكلمات. هذا مثالٌ عن ناتج البرنامج: What is the password? hello What is the password? sammy What is the password? PASSWORD What is the password? password Yes, the password is password. You may enter. أبقِ في ذهنك أنَّ السلاسل النصية حساسة لحالة الأحرف إلا إذا استعملتَ دالةً من دوال النصوص لتحويل السلسلة النصية إلى حالة الأحرف الصغيرة (على سبيل المثال) قبل التحقق منها. مثال عن برنامج يستخدم حلقة while بعد أن تعلمنا المبدأ الأساسي لحلقة تكرار while، فلنُنشِئ لعبة تعمل على سطر الأوامر لتخمين الأرقام والتي تستعمل الحلقة while . نريد من الحاسوب أن يُنشِئ أرقامًا عشوائيةً لكي يحاول المستخدمون تخمينها، لذا علينا استيراد الوحدة random عبر استخدام العبارة import، وإذا لم تكن هذه الحزمة مألوفةً لك فيمكنك قراءة المزيد من المعلومات عن توليد الأرقام العشوائية في توثيق بايثون. لنُنشِئ بدايةً ملفًا باسم guess.py في محررك النصي المفضَّل: import random علينا الآن إسناد عدد صحيح عشوائي إلى المتغير number، ولنجعل مجاله من 1 إلى 25 (بما فيها تلك الأرقام) كيلا نجعل اللعبة صعبة جدًا. import random number = random.randint(1, 25) يمكننا الآن إنشاء حلقة while، وذلك بتهيئة متغير ثم كتابة الحلقة: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess == number: break هيئنا متغيرًا اسمه number_of_guesses قيمته 0، وسوف نزيد قيمته عند كل تكرار للحلقة لكي لا تصبح حلقتنا لا نهائية. ثم سنضيف تعبير while الذي يشترط ألّا تزيد قيمة المتغير number_of_guesses عن 5. وبعد المحاولة الخامسة سيُعاد المستخدم إلى سطر الأوامر، وإذا حاول المستخدم إدخال أيّ شيء غير رقمي فسيحصل على رسالة خطأ. أضفنا داخل حلقة while عبارة print()‎ لطلب إدخال رقم من المستخدم، ثم سنأخذ مدخلات المستخدم عبر الدالة input()‎ ونُسنِدَها إلى المتغير guess، ثم سنحوِّل المتغير guess من سلسلة نصية إلى عدد صحيح. وقبل انتهاء حلقة التكرار، فعلينا زيادة قيمة المتغير number_of_guesses بمقدار 1، لكيلا تُنفَّذ حلقة التكرار أكثر من 5 مرات. وفي النهاية، كتبنا عبارة if شرطية لنرى إذا كان المتغير guess الذي أدخله المستخدم مساوٍ للرقم الموجود في المتغير number الذي ولَّده الحاسوب، وإذا تحقق الشرط فسنستخدم عبارة break للخروج من الحلقة. أصبح البرنامج جاهزًا للاستخدام، ويمكننا تشغيله عبر تنفيذ الأمر: python guess.py صحيحٌ أنَّ البرنامج يعمل عملًا سليمًا، لكن المستخدم لن يعلم إذا كان تخمينه صحيحًا ويمكنه أن يخمِّن الرقم خمس مرات دون أن يعلم إذا كانت إحدى محاولاته صحيحة. هذا مثال عن مخرجات البرنامج: Guess a number between 1 and 25: 11 Guess a number between 1 and 25: 19 Guess a number between 1 and 25: 22 Guess a number between 1 and 25: 3 Guess a number between 1 and 25: 8 لنضف بعض العبارات الشرطية خارج حلقة التكرار لكي يحصل المستخدم على معلومات فيما إذا استطاعوا تخمين الرقم أم لا، وسنضيف هذه العبارات في نهاية الملف: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess == number: break if guess == number: print('You guessed the number in ' + str(number_of_guesses) + ' tries!') else: print('You did not guess the number. The number was ' + str(number)) في هذه المرحلة سيُخبِر البرنامجُ المستخدمَ إذا استطاعوا تخمين الرقم، لكن ذلك لن يحدث إلا بعد انتهاء حلقة التكرار وبعد انتهاء عدد مرات التخمين المسموحة. ولمساعد المستخدم قليلًا، فلنضف بعض العبارات الشرطية داخل حلقة while وتلك العبارات ستخبر المستخدم إذا كان تخمينه أعلى من الرقم أو أصغر منه، لكي يستطيعوا تخمين الرقم بنجاح، وسنضيف تلك العبارات الشرطية قبل السطر الذي يحتوي على if guess == number: import random number = random.randint(1, 25) number_of_guesses = 0 while number_of_guesses < 5: print('Guess a number between 1 and 25:') guess = input() guess = int(guess) number_of_guesses = number_of_guesses + 1 if guess < number: print('Your guess is too low') if guess > number: print('Your guess is too high') if guess == number: break if guess == number: print('You guessed the number in ' + str(number_of_guesses) + ' tries!') else: print('You did not guess the number. The number was ' + str(number)) وعندما نُشغِّل البرنامج مرةً أخرى بتنفيذ python guess.py، فيمكننا ملاحظة أنَّ المستخدم سيحصل على بعض المساعدة، فلو كان الرقم المولَّد عشوائيًا هو 12 وكان تخمين المستخدم 18، فسيُخبره البرنامج أنَّ الرقم الذي خمنه أكبر من الرقم العشوائي، وذلك لكي يستطيع تعديل تخمنيه وفقًا لذلك. هنالك الكثير من التحسينات التي يمكن إجراؤها على الشيفرة السابقة، مثل تضمين آلية لمعالجة الأخطاء التي تحدث عندما لا يُدخِل المستخدم عددًا صحيحًا، لكن كان غرضنا هو رؤية كيفية استخدام حلقة while في برنامج قصير ومفيد يعمل من سطر الأوامر. الخلاصة شرحنا في هذا الدرس كيف تعمل حلقات while في بايثون وكيفية إنشائها. حيث تستمر حلقات while بتنفيذ مجموعة من الأسطر البرمجية لطالما كان الشرط مساويًا للقيمة true. ترجمة –وبتصرّف– للمقال How To Construct While Loops in Python 3 لصاحبته Lisa Tagliaferri
  5. تمهيد يبدو نوع البيانات tuple في بايثون كما يلي: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') tuple هي بنية بيانات تُمثِّل سلسلة مرتبة من العناصر غير القابلة للتبديل، وبالتالي لا يمكن تعديل القيم الموجودة فيها. يستعمل نوع البيانات tuple لتجميع البيانات، فكل عنصر أو قيمة داخل tuple تُشكِّل جزءًا منه. توضع القيم داخل نوع البيانات tuple بين قوسين ( ) ويُفصَل بينها بفاصلة ,، وتبدو القيم الفارغة كما يلي coral = ()‎، لكن إذا احتوى نوع البيانات tuple على قيم –حتى لو كانت قيمةً واحدةً فقط– فيجب وضع فاصلة فيه مثل coral = ('blue coral',). إذا استخدمنا الدالة print()‎ على tuple، فسنحصل على الناتج الآتي الذي يُبيّن أنَّ القيمة الناتجة ستوضع بين قوسين: print(coral) ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') عند التفكير بنوع tuple وغيره من بنى البيانات التي تُعبَر من أنوع «المجموعات» (collections)، فمن المفيد أن تضع ببالك مختلف المجموعات الموجودة في حاسوبك: تشكيلة الملفات الموجودة عندك، وقوائم التشغيل للموسيقى، والمفضلة الموجودة في متصفحك، ورسائل بريدك الإلكتروني، ومجموعة مقاطع الفيديو التي تستطيع الوصول إليها من التلفاز، والكثير. نوع tuple شبيه بالقوائم (lists)، لكن القيم الموجودة فيه لا يمكن تعديلها، وبسبب ذلك، فأنت تخبر الآخرين أنَّك لا تريد إجراء أيّة تعديلات على هذه السلسلة من القيم عندما تستعمل tuple في شيفرتك. إضافةً إلى ما سبق، ولعدم القدرة على تعديل القيم، فسيكون أداء برنامجك أفضل، حيث ستُنفَّذ الشيفرة بشكل أسرع إذا استعملتَ tuple بدلًا من القوائم (lists). فهرسة نوع البيانات tuple يمكن الوصول إلى كل عنصر من عناصر tuple بمفرده لأنَّه سلسلة مرتبة من العناصر، وذلك عبر الفهرسة. وكل عنصر يرتبط برقم فهرس، الذي هو عدد صحيح يبدأ من الفهرس 0. لمثال coral السابق، ستبدو الفهارس والقيم المرتبطة بها كالآتي: ‘blue coral’ ‘staghorn coral’ ‘pillar coral’ ‘elkhorn coral’ 0 1 2 3 العنصر الأول الذي يُمثِّل السلسلة النصية 'blue coral' تبدأ بالفهرس 0، وتنتهي القائمة بالفهرس رقم 3 المرتبط بالقيمة 'elkhorn coral'. ولأن كل عنصر من عناصر tuple له رقم فهرس مرتبط به، فسنتمكن من الوصول إلى عناصره فرادى. يمكننا الآن الوصول إلى عنصر معيّن في tuple عبر استخدام رقم الفهرس المرتبط به. print(coral[2]) pillar coral تتراوح قيم الفهارس في المتغير coral من 0 إلى 3 كما هو ظاهر في الجدول السابق، لذا يمكننا استدعاء العناصر الموجودة فيه فرادى كما يلي: coral[0] coral[1] coral[2] coral[3] إذا حاولنا استدعاء المتغير coral مع رقم فهرس أكبر من 3، فستظهر رسالة خطأ تشير إلى أنَّ الفهرس خارج المجال: print(coral[22]) IndexError: tuple index out of range إضافةً إلى أرقام الفهارس الموجبة، يمكننا أيضًا الوصول إلى الفهارس باستخدام رقم فهرس سالب، وذلك بالعد بدءًا من نهاية قائمة العناصر وسيرتبط آخر عنصر بالفهرس ‎-1، وهذا مفيدٌ جدًا إذا كان لديك متغير من النوع tuple وكان يحتوي عناصر كثيرة وأردتَ الوصول إلى أحد عناصره انطلاقًا من النهاية. ففي مثالنا السابق عن coral، إذا أردنا استخدام الفهارس السالبة فالناتج كالآتي: ‘elkhorn coral’ ‘pillar coral’ ‘staghorn coral’ ‘blue coral’ -1 -2 -3 -4 إذا أردنا طباعة العنصر 'blue coral' باستخدام الفهارس السالبة، فستبدو التعليمة كما يلي: print(coral[-4]) blue coral يمكننا إضافة العناصر النصية الموجودة في tuple إلى السلاسل النصية الأخرى باستخدام المعامل +: print('This reef is made up of ' + coral[1]) This reef is made up of staghorn coral استطعنا في المثال السابق إضافة عنصر موجود في الفهرس 1 مع السلسلة النصية 'This reef is made up of '، ويمكننا أيضًا استخدام المعامل + لإضافة بنيتَي tuple معًا. الخلاصة: يمكننا الوصول إلى كل عنصر من عناصر tuple على حدة باستخدام أرقام الفهارس (الموجبة أو السالبة) المرتبطة بها. تقطيع قيم tuple يمكننا استخدام الفهارس للوصول إلى عدِّة عناصر من tuple، أما التقطيع فيسمح لنا بالوصول إلى عدِّة قيم عبر إنشاء مجال من أرقام الفهارس المفصولة بنقطتين رأسيتين [x:y]. لنقل أننا نريد عرض العناصر الموجودة في وسط المتغير coral، يمكننا فعل ذلك بإنشاء قطعة جديدة: print(coral[1:3]) ('staghorn coral', 'pillar coral') عند إنشاء قطعة جديدة –كما في المثال السابق– فيمثِّل أوّل رقم مكان بدأ القطعة (متضمنةً هذا الفهرس)، ورقم الفهرس الثاني هو مكان نهاية القطعة (دون تضمين هذا الفهرس بالقطعة)، وهذا هو السبب وراء عرض المثال السابق للقيم المرتبطة بالعناصر الموجودة في الفهرسين 1 و 2. إذا أردتَ تضمين إحدى نهايتَي القائمة، فيمكنك حذف أحد الأرقام في التعبير tuple[x:y]، فمثلًا، لنقل أننا نريد عرض أوّل ثلاثة عناصر من coral، والتي هي 'blue coral' و 'staghorn coral' و 'pillar coral'، فيمكننا فعل ذلك كالآتي: print(coral[:3]) ('blue coral', 'staghorn coral', 'pillar coral') المثال السابق عرض العناصر من بداية القائمة وتوقف قبل العنصر ذي الفهرس 3. لتضمين كل العناصر الموجودة في نهاية tuple، فيمكننا عكس التعبير السابق: print(coral[1:]) ('staghorn coral', 'pillar coral', 'elkhorn coral') يمكننا استخدام الفهارس السالبة أيضًا عند التقطيع، كما فعلنا مع أرقام الفهارس الموجبة: print(coral[-3:-1]) print(coral[-2:]) ('staghorn coral', 'pillar coral') ('pillar coral', 'elkhorn coral') هنالك معاملٌ إضافيٌ يمكننا استعماله ويسمى «الخطوة»، ويُشير إلى عدد العناصر التي يجب تجاوزها بعد الحصول على أوّل عنصر من القائمة. حذفنا في جميع أمثلتنا السابقة معامل الخطوة، حيث القيمة الافتراضية له في بايثون هي 1، لذا سنحصل على جميع العناصر الموجودة بين الفهرسَين المذكورين. شكل هذا التعبير العام هو tuple[x:y:z]، إذ يُشير المعامل z إلى الخطوة. لنُنشِئ قائمةً أكبر، ثم نقسِّمها، ونعطيها القيمة 2 كخطوة: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) print(numbers[1:11:2]) (1, 3, 5, 7, 9) التعبير numbers[1:11:2] سيطبع القيم الموجودة بين رقمين الفهرسين 1 (بما في ذلك العنصر المرتبط بالفهرس 1) و 11 (دون تضمين ذلك العنصر)، ومن ثم ستخبر قيمةُ الخطوة 2 البرنامجَ أنَّ يتخطى عنصرًا بين كل عنصرين. يمكننا حذف أوّل معاملين واستخدام معامل الخطوة بمفرده بتعبيرٍ برمجيٍ من الشكل tuple[::z]: print(numbers[::3]) (0, 3, 6, 9, 12) طبعنا في المثال السابق عناصر numbers بعد ضبط قيمة الخطوة إلى 3، وبالتالي سيتم تخطي عنصرين. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 الخلاصة: تقطيع tuples باستخدام أرقام الفهارس الموجبة والسالبة واستعمال معامل الخطوة يسمح لنا بالتحكم بالناتج الذي نريد عرضه. إضافة بنى tuple إلى بعضها يمكن أن نُضيف بنى tuple إلى بعضها أو أن «نضربها» (multiply)، تتم عملية الإضافة باستخدام المعامل +، أما عملية الضرب فباستخدام المعامل *. يمكن أن يُستخدَم المعامل + لإضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا. يمكننا إسناد القيم الموجودة في بنيتَي tuple إلى بنية جديدة: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') coral_kelp = (coral + kelp) print(coral_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') وصحيحٌ أنَّ المعامل + يمكنه إضافة بنى tuple إلى بعضها، لكن يمكن أن يستعمل لإنشاء بنية tuple جديدة ناتجة عن جمع بنى أخرى، لكن لا يمكنه تعديل بنية tuple موجودة مسبقًا. أما المعامل * فيمكن استخدامه لضرب بنى tuple، فربما تريد إنشاء نسخ من الملفات الموجودة في أحد المجلدات إلى الخادوم أو مشاركة قائمة بالمقطوعات الموسيقية التي تحبها مع أصدقائك، ففي هذه الحالات سترغب بمضاعفة مجموعات من البيانات (أو «ضربها»). لنضرب البنية coral بالرقم 2 والبنية kelp بالرقم 3، ثم نسندها إلى بنى tuple جديدة: multiplied_coral = coral * 2 multiplied_kelp = kelp * 3 print(multiplied_coral) print(multiplied_kelp) الناتج: ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral', 'blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis', 'wakame', 'alaria', 'deep-sea tangle', 'macrocystis') يمكننا باستخدام المعامل * أن نُكرِّر (أو نُضاعِف) بنى tuple بأي عدد من المرات نشاء، مما سينُشِئ بنى tuple جديدة اعتمادًا على محتوى البنى الأصلية. الخلاصة هي أنَّ بنى tuple يمكن إضافتها إلى بعضها أو ضربها لتشكيل بنى tuple جديدة عبر استخدام المعاملَين + و *. دوال التعامل مع tuple هنالك دوال مُضمَّنة في لغة بايثون للتعامل مع بنى tuple، لننظر إلى بعضها. len()‎ وكما في السلاسل النصية والقوائم، يمكننا حساب طول (أو عدد عناصر) بنية tuple باستخدام الدالة len()‎ حيث نُمرِّر إليها بنية tuple كمعامل (parameter)، كما يلي: len(coral) هذه الدالة مفيدة إذا أردنا أن نَضمَن أنَّ لبنية tuple عدد عناصر معيّن، فمثلًا يمكننا الاستفادة من ذلك بمقارنة بنيتين مع بعضهما. إذا أردنا طباعة عدد عناصر kelp و numbers، فسيظهر الناتج الآتي: print(len(kelp)) print(len(numbers)) الناتج: 4 13 الناتج أعلاه يشير إلى أنَّ للبنية kelp أربعة عناصر: kelp = ('wakame', 'alaria', 'deep-sea tangle', 'macrocystis') أما البنية numbers فتملك ثلاثة عشر عنصرًا: numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) وصحيحٌ أنَّ هذه الأمثلة عناصرها قليلة نسبيًا، إلا أنَّ الدالة len()‎ تستطيع أن تخبرنا بعدد عناصر بنى tuple الكبيرة. الدالتان max()‎ و min()‎ عندما نتعامل مع بنى tuple مكوَّنة من عناصر رقمية (بما فيها الأعداد الصحيحة والأرقام ذات الفاصلة العشرية)، فيمكننا استخدام الدالتين max()‎ و min()‎ للعثور على أكبر وأصغر قيمة موجودة في بنية tuple معيّنة. تسمح لنا هاتان الدالتان باستخراج معلومات تخص البيانات القابلة للإحصاء، مثل نتائج الامتحانات أو درجات الحرارة أو أسعار المنتجات …إلخ. لننظر إلى بنية tuple مكونة من أعداد عشرية: more_numbers = (11.13, 34.87, 95.59, 82.49, 42.73, 11.12, 95.57) للحصول على القيمة العظمى من بين القيم الآتية فعلينا تمرير بنية tuple إلى الدالة max()‎ كما في max(more_numbers)، وسنستخدم الدالة print()‎ لعرض الناتج: print(max(more_numbers)) 95.59 أعادت الدالة max()‎ أعلى قيمة في بنية more_numbers. وبشكلٍ شبيهٍ بما سبق نستخدم الدالة min()‎: print(min(more_numbers)) 11.12 أُعيدَ هنا أصغر رقم عشري موجودة في البنية. يمكن الاستفادة من الدالتين max()‎ و min()‎ كثيرًا للتعامل مع بنى tuple التي تحتوي الكثير من القيم. كيف تختلف بنى tuple عن القوائم (list) الفرق الرئيسي بين tuple و list هو عدم القدرة على تعديلها، وهذا يعني أنَّنا لا نستطيع إضافة أو حذف أو استبدال العناصر داخل بنية tuple. لكن يمكننا إضافة بنيتَي tuple أو أكثر إلى بعضها بعضًا لتشكيل بنية جديدة كما رأينا في أحد الأقسام السابقة. لتكن لدينا البنية coral الآتية: coral = ('blue coral', 'staghorn coral', 'pillar coral', 'elkhorn coral') لنقل أننا نريد استبدال العنصر 'blue coral' ووضع العنصر 'black coral' بدلًا منه. فلو حاولنا تغيير بنية tuple بنفس الطريقة التي نُعدِّل فيها القوائم بكتابة: coral[0] = 'black coral' فستظهر رسالة خطأ كالآتية: TypeError: 'tuple' object does not support item assignment وذلك بسبب عدم إمكانية تعديل بنى tuple. إذا أنشأنا بنية tuple ثم قررنا أنَّ ما نحتاج له هو بنية list، فيمكننا تحويلها إلى قائمة list، وذلك بالدالة list()‎: list(coral) أصبحت بنية coral قائمةً الآن: coral = ['blue coral', 'staghorn coral', 'pillar coral'] يمكننا أن نلاحظ أنَّ بنية tuple تحوَّلتَ إلى قائمة list لأنَّ الأقواس المحيطة بالقيم أصبح مربعة الشكل. وبشكلٍ شبيهٍ بما سبق، نستطيع تحويل القوائم من النوع list إلى tuple باستخدام الدالة tuple()‎. الخلاصة نوع البيانات tuple هو مجموعةٌ من البيانات المتسلسلة التي لا يمكن تعديلها، ويوفِّر تحسينًا في أداء برامجك لأنه أسرع معالجةً من القوائم في بايثون. وعندما يراجع الآخرون شيفرتك فسيعلمون من استخدامك لبنى tuple أنك لا تريد تعديل هذه القيم. شرحنا في هذا الدرس الميزات الأساسية لبنى tuple بما في ذلك الفهارس وتقطيعها وتجميعها، وعرضنا بعض الدوال المُضمَّنة المتوافرة لهذا النوع من البيانات. ترجمة –وبتصرّف– للمقال Understanding Tuples in Python 3لصاحبته Lisa Tagliaferri
  6. flask_cms

    مُقدّمة تعرّفنا سابقا على حلقة التّكرار for للدّوران على عناصر قائمة أو مفاتيح وقيم قاموس بايثون، سنتعرّف الآن على تقنيات مُتقدّمة ستُساعدك على التّعامل مع حلقة for على نحو أفضل لتتمكّن من تصميم قوالب HTML مع مُحرّك القوالب Jinja بطريقة أسرع وأكثر إتقانا. استعمال حلقة 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 فستكون النّتيجة مُشابهة لما في الصّورة التّاليّة: لاحظ بأنّ القاموس غير مُرتّب على نحو صحيح، ذلك لأنّ قواميس بايثون ليست مُرتّبة مبدئيًّا، لكن يُمكنك ترتيب مُحتويات القاموس أبجديا عبر استعمال المُرشّح 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> النّتيجة ستكون كما في الصّورة التّاليّة: يُمكنك كذلك معرفة عمق الحلقة باستخدام التّابع loop.depth داخل حلقة for من النّوع recursive كالتّالي: <span class="badge">{{ loop.depth }} </span> تأكّد فقط من أن تضع السّطر السّابق مُباشرة بعد السّطر: {% for person in people recursive %} النّتيجة ستكون كالتّالي: لاحظ بأنّ رقم العمق يبدأ من واحد، إن أردت أن يبدأ من الصّفر فاستعمل loop.depth0. يُمكنك كذلك استخدام هذه الميّزة مع تعليقات في مُدوّنة أو منصّة تتطلّب تفاعل الأعضاء بين بعضهم البعض. الصّورة التّاليّة توضيح بسيط للنّتيجة النّهائيّة التي يُمكنك أن تصل إليها إن كنت تمتلك تعليقات متداخلة في قاعدة بياناتك: خاتمة تعرّفنا في هذا الدّرس على كيفيّة استخدام حلقة for في مُحرّك القوالب Jinja للحصول على قوالب HTML أحسن وتطويرها بسرعة. وبهذا نكون قد أكملنا أساسيّات دروس مُحرّك القوالب Jinja وسننتقل في الدّروس القادمة للتّعرف على جزء آخر مُهمّ في تطوير الويب، ألا وهو كيفيّة التّعامل مع قواعد البيانات في التّطبيقات المكتوبة بإطار العمل Flask.
  7. flask_cms

    مُقدّمة تعرّفنا في الدّرس السّابق على التعليمة include وكيفيّة استخدامها في مُحرّك Jinja لتضمين قالب HTML داخل قالب آخر، في هذا الدّرس سنتعرّف على ميّزة أخرى ستُساعدك على تفادي تكرار الشّيفرة، إذ سنتعرّف على تعليمة import لاستيراد مكوّن ما من قالب واستعماله في قالب آخر. ما الغرض من الاستيراد؟ تعرّفنا مُسبقا على خاصّية الماكرو في Jinja، وقلنا بأنّها مُفيدة لتفادي تكرار الشّفرة وأنّها تُستخدم كما تُستخدم الدّوال في لغة بايثون، لكنّنا لم نتطرّق بالتّفصيل إلى جزئيّة مُهمّة، ألا وهي كيفيّة جمع عدّة دوال في قالب واحد ليعمل مثل الوحدة (Module) في بايثون، بحيث يشمل عدّة دوال يُمكنك استيرادها بسطر واحد واستخدامها في أكثر من قالب واحد عوضا عن كتابة الماكرو أعلى الصّفحة. يُمكنك كذلك استخدام قالب HTML مُعيّن كمخزن لإعدادات تطبيق، مثل اسم التّطبيق والمُعاملات التّي يُمكنك بها التّحكّم في جزئيّات مُعيّنة ممّا يُعرض على مُتصفّح المُستخدم، ومن ثمّ يُمكنك استيراد المُعاملات من هذا القالب واستغلالها في بقيّة القوالب كما تشاء لتوفير ديناميكيّة أفضل لتطبيقك. التعليمة import لاستيراد مُتغيّر أو ماكرو من قالب مُعيّن كتابة شيفرة نماذج HTML أمر مُملّ، لذا قد ترغب في كتابة بعض الدّوال لتوليد النّماذج بشكل سريع اعتمادا على مُعاملات مُعيّنة. لنفترض بأنّ لدينا ما يلي في مُجلّد templates الخاصّ بتطبيق Flask: ملفّ base.html . ملفّ index.html للصّفحة الرّئيسية. ملفّ باسم forms.html. سيحتوي ملفّ base.html على شفرة HTML بسيطة: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <title>{% block title %}{% endblock %}</title> </head> <body> {% block body %} {% endblock %} </body> </html> أمّا ملفّ index.html فسيرث من الملفّ السّابق بالإضافة إلى عنوان داخل وسم <h1> لطلب تسجيل الدّخول من المُستخدم: {% extends 'base.html' %} {% block title %} My Application {% endblock %} {% block body %} <div class="container"> <h1>Please Login</h1> </div> {% endblock %} أما forms.html فسيحتوي على ماكرو خاصّ بعرض النّماذج من نوع input و ماكرو آخر لعرض نموذج لمساحة النّص (TextArea): {% macro input(name, value='', type='text', placeholder='') %} <input class="form-control" type="{{ type }}" value="{{ value|e }}" name="{{ name }}" placeholder="{{ placeholder }}"> {% endmacro %} {% macro textarea(name, value='', rows=10, cols=40, placeholder='') %} <textarea class="form-control" name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}" placeholder="{{ placeholder }}">{{ value|e }}</textarea> {% endmacro %} ينشئ الماكرو الأول حقل إدخال من نوع input، يقبل الماكرو ثلاثة مُعاملات: name لاسم الحقل. value لقيمة الحقل المبدئية. type لنوع الحقل (نصّ عادي، كلمة مرور، …إلخ). placeholder للنّص المبدئي. تُستعمل هذه المُعاملات إن وفّرها المُطوّر للتّحكم بكيفيّة توليد الحقل، فمثلا القيمة المبدئية للحقل type هي text ما يعني بأنّك إن لم تُمرّر قيمةً لهذا المُعامل فسيُولّد الماكرو حقلا نصّيا عاديّا، ولو غيّرت القيمة مثلا إلى password فسيكون الحقل المُولّد عبارة عن حقل كلمة مرور. قد تُلاحظ بأنّنا نستخدم المُرشّح e الذي هو اختصار لكلمة escape والذي يقوم بحماية الحقل من الهجمات الخبيثة عبر منع ترجمة شفرات HTML وJavascript. أمّا بالنّسبة للماكرو textarea فهو مُشابه لما سبق، إلّا أنّه يُولّد وسما من النّوع textareaالذي يُعبّر عن حقل لنصّ مُتعدّد الأسطر، كما أنّه يقبل كلّا من المُعاملين rows و cols لتحديد حجم مساحة النّص التي ستظهر في صفحة HTML. استعمال التعليمة import لاستيراد الوحدة forms.html بعد أن أنشأنا قاعدة لمثالنا، وبعد أن كتبنا دوال خاصّة بتوليد حقول الإدخال وجمعها في ملفّ forms.html لتُمثّل وحدة كما في وحدات Python، لنستعملها في ملفّ index.html. أولا، ضع سطر الاستيراد في رأس صفحة index.html: {% import 'forms.html' as forms %} لاحظ بأنّنا استعملنا الكلمة المفتاحية as لإسناد اسم مُستعار للوحدة المُستوردة بحيث نتمكّن من الوصول إليها عبر الاسم forms. بعد استيراد الوحدة، يُمكنك استدعاء الدّوال المُتواجدة بداخلها كما يلي: forms.input() forms.textarea() لكن ستحتاج إلى تمرير مُعاملات حسب نوع الحقل الذي ترغب بتوليده. لنُضف الآن بضعة حقول إلى ملفّ index.html باستخدام الوحدة التّي قمنا باستيرادها: {{ forms.input('username', placeholder='Type in your username') }} <br> {{ forms.input('password', placeholder='Type in your password', type='password') }} <br> {{ forms.textarea('message', placeholder='Write your message here.') }} <br> <button class="btn btn-primary">Submit</button> في الشّيفرة أعلاه، نستعمل الماكرو forms.input لإنشاء حقل نصي لاسم المُستخدم، ونستخدمه مُجدّدا لإنشاء حقل لكلمة المرور الذي حدّدنا نوعه بالقيمة password، ثمّ بعد ذلك أنشأنا حقلا لمساحة النّص مع تعيين القيمة message كاسم للحقل، وفي آخر سطر أضفنا زرّا صغيرا لإرسال مُحتويات النّموذج. بعد إضافة الشّفرة السّابقة، ستكون مُحتويات الملفّ index.html كما يلي: {% import 'forms.html' as forms %} {% extends 'base.html' %} {% block title %} My Application {% endblock %} {% block body %} <div class="container"> <h1>Please Login</h1> {{ forms.input('username', placeholder='Type in your username') }} <br> {{ forms.input('password', placeholder='Type in your password', type='password') }} <br> {{ forms.textarea('message', placeholder='Write your message here.') }} <br> <button class="btn btn-primary">Submit</button> </div> {% endblock %} والصّفحة ستكون مُشابهة للمثال في الصّورة التّالية: مُلاحظة: الغرض من هذه الأمثلة هو شرح مبدأ الاستيراد وكيفية عمله في مُحرّك القوالب Jinja. إن أردتَ إدارة نماذج HTML على نحو أفضل وأكثر أمانا، فمن الأفضل استخدام مكتبة WTForms. استيراد مكوّن مُعيّن باستخدام التّعبير from import بالإضافة إلى إمكانيّة استيراد وحدة واستعمال الدّوال المتواجدة فيها كما يلي: import module module.macro() يُمكنك كذلك استخدام التّعبير from import لاستيراد ماكرو واحد فقط كما في المثال التّالي: {% from 'forms.html' import input %} هكذا ستتمكّن من استعمال الماكرو input عبر استدعائه، لكنّك لن تستطيع الوصول إلى الماكرو textarea حتى تستدعيه على نحو صريح. لاستدعاء أكثر من ماكرو واحد، افصل بينها بفاصلة كالتّالي: {% from 'forms.html' import input, textarea %} يُمكنك كذلك استعمال الكلمة المفتاحية as لمنح اسم مُستعار لكلّ ماكرو: {% from 'forms.html' import input as i, textarea as t %} بعد استيراد الماكرو باسم مُستعار، يُمكنك استخدام هذا الاسم المُستعار كما يلي: {{ i() }} {{ t() }} خاتمة بنهاية هذا الدّرس، يجب أن تكون قادرا على تصميم قوالب HTML بمرونة أكثر، بحيث تتمكّن من استغلال الجملة import لتفادي تكرار الشّيفرة والحصول على تجربة تطوير أفضل.
  8. مُقدّمة تكرار الشّيفرة من أسوء المُمارسات التّي يُمكن لأي مُطوّر القيام بها، إذ تستهلك الكثير من الوقت الثّمين الّذي يُمكن صرفه في تطوير ميّزات أخرى وأمور أكثر أهميّة؛ سنتعرّف في هذا الدّرس على كيفيّة استغلال مُحرّك القوالب Jinja لتفادي تكرار الشّيفرة وتقسيم ملفّ HTML كبير إلى عدّة ملفّات صغيرة يُمكن إعادة استعمالها عبر الاعتماد على التعليمة include في Jinja. التعليمة include لتضمين قالب HTML داخل آخر تُستعمل التعليمة include لتضمين مُحتويات ملفّ HTML في مكان ما في قالب HTML آخر. على سبيل المثال، لنقل بأنّ لدينا خمسة قوالب HTML في مجلّد templates الخاصّ بنا كما يلي: ملفّ باسم base.html ملفّ باسم header.html ملفّ آخر باسم content.html ملفّ آخر باسم footer.html الملفّ الرّئيسي باسم main.html هكذا ستكون الصّورة العامّة للملفّات داخل المُجلّد templates كما يلي: └── templates ├── body.html ├── footer.html ├── header.html └── main.html └── base.html بالنّسبة للقالب base.html فهو معروف وطريقة استعماله واضحة، إذ يُعتبر القاعدة الأساسيّة لصفحات HTML التّي تعتمد عليه، ومُكوّناته ستكون كالتّالي: <!DOCTYPE html> <html lang="en"> <head> <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> </head> <body> {% block body %} {% endblock %} </body> </html> كما تُلاحظ هنا فإنّنا نقوم بوضع شِفرة HTML التّي تتكرّر في كلّ صفحة وفيها جزءان ديناميكيّان، الجزء الأول يُعنى بعنوان الصّفحة ويوجد داخل الوسم <title> وأمّا الجزء الثّاني فمسؤول عن الظّهور داخل جسم الصّفحة بالوسم <body>، وسنرى كيفيّة استغلال هذا القالب بعد لحظات. لاحظ كذلك بأنّنا أضفنا ملفّ CSS الخاص بإطار العمل Bootstrap في وسم <link> في الشّفرة أعلاه. أمّا عن قالب header.html فسيحمل شِفرة HTML التّي تكون عادة داخل الوسم <header> والتّي تكون مسؤولة عن الجزء العلوي من صفحة HTML كشريط التّصفّح وشعار التّطبيق وغير ذلك من المُكونات التّي تكون أعلى صفحات الويب. شيفرة هذا الملفّ ستكون عادة مُشابهة لما يلي: <header> <div class="navbar navbar-default"> <div class="container-fluid"> <ul class="nav navbar-nav"> <li><a class="navbar-brand" href="#"> Home </a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="/about/"> <span class="glyphicon glyphicon-info-sign"></span> About </a></li> <li><a href="/contact/"> <span class="glyphicon glyphicon-envelope"></span> Contact </a></li> </ul> </div> </div> </header> إن لم تفهم الشّفرة أعلاه فلا تقلق، إذ سيتّضح كلّ شيء بعد أن نجمع الملفّات بعضها مع بعض. لكن عليك أن تفهم فقط بأنّ الشّفرة بالأعلى عبارة عن شفرة HTML مع تنسيقات من إطار العمل Bootstrap تُمثّل شريط تصفّح به ثلاثة أزرار، الزّر Home للرّبط بالصّفحة الرّئيسية، والزّر About لصفحة “عن التّطبيق” والزّر Contact لصفحة التّواصل. أمّا ملفّ footer.html فسيحمل الشّفرة التّي تكون عادة أسفل صفحات الوِب مثل حقوق المُحتوى ومعلومات عن الشّركة صاحبة الموقع أو عن فريق التّطوير الّذي ساهم في بناء التّطبيق، بالإضافة إلى إمكانيّة احتوائه على معلومات التّواصل مع أصحاب التّطبيق أو معلومات أخرى، وقد تُلاحظ أسفل هذا الموقع (موقع أكاديميّة حسوب)، جزءا خاصّا بمُنتجات شركة حسوب وقد تُلاحظ ذلك في الصّفحات الأخرى التّابعة لمُنتجات حسوب، وهذا ما أقصده بالضّبط. كمثال على مُحتوى هذا الملفّ، سنستعمل الشِّفرة التّاليّة لعرض حقوق الموقع واسم المُطوّر: <footer class="footer"> <div class="container"> <p>Built by Abdelhadi Dyouri</p> <p class="text-muted">All rights reserved.</p> </div> </footer> بالنّسبة للملفّ content.html، فسيكون المسؤول عمّا بداخل الوسم <div> ذي خاصيّة CSS المسمّاة container التّي يُوفّرها إطار العمل Bootstrap. سيشمل هذا الجزء المُكونات التي تُمثّل المُحتوى العام للموقع، فمثلا إن كان الموقع معنيّا بنشر المقالات، فالمُحتوى الّذي سيكون بداخل هذا الوسم هو لائحة المقالات وإن كانت الصّفحة خاصّة بعرض مقال واحد، فعنوان هذا المقال ومُحتواه وكاتبه والمعلومات العامّة حول المقال ستكون داخل هذا الوسم. ما يُميّز هذا الجزء من صفحات HTML هو أنّه يكون مُتغيّرا عكس كلّ من رأس الصّفحة وأسفلها، إذ يبقى مثلا شريط التّصفّح هو نفسه في جميع صفحات التّطبيق وكذلك مُكوّنات أسفل الصّفحة كالحقوق والمعلومات حول التّطبيق، إذ يُمكن للمقالات في وسط الصّفحة أن تتبدّل وأن يتغيّر نوع المحتوى من مقال إلى صفحة لاستعراض المعلومات الشّخصيّة للكاتب وما إلى ذلك. على سبيل المثال، سنستعمل المثال الّذي سبق وأن شرحناه في درس الدّالتين range وlipsum لملْء مُحتوى هذا الملفّ: <div class='container'> {% for i in range(8) %} <div class='col-md-3'> <h2> <a href='#'> {{ lipsum(1, min=3, max=5) }} </a> </h2> {{ lipsum(1, max=50) }} <button class='btn btn-default'>Read more...</button> </div> {% endfor %} </div> إن لم تقرأ الدّرس من قبل، فلا تقلق، المهم أن تفهم بأنّ الشّفرة أعلاه تُنتج ثمانيّة فقرات مع عنوان باستعمال نصّ lorem ipsum، ما يُتيح لنا الحصول على مُحتوى يُشبه المقالات دون كتابة شيفرة HTML كبيرة ومُكرّرة مكان استعمال دوال في Jinja للقيام بالأمر بكلّ بساطة. بالنّسبة للملفّ main.html، فهو الملفّ الرّئيسي الّذي سنستعمله لجمع هذه الملفّات لتظهر على شكل صفحة واحدة. الشّيء الّذي سنقوم به في الفقرة التّالية. جمع ملفّات HTML لتظهر في صفحة واحدة باستخدام التعليمة include بعد أن أصبح لدينا كلّ من الجزء العلوي للموقع في ملفّ header.html ومُحتوى الموقع في الملفّ content.html والجزء السّفلي للصّفحة داخل ملفّ footer.html، حان الوقت لجمع كلّ هذه المُكونات في ملفّ واحد لتجتمع في صفحة واحدة. سنستخدم الملفّ main.html لجمع جميع هذه المُكوّنات وسنستعمل التعليمة include كما شرحنا مُسبقا: {% extends 'base.html' %} {% block title %} My Application {% endblock %} {% block body %} {% include 'header.html' %} {% include 'content.html' %} {% include 'footer.html' %} {% endblock %} كما تُلاحظ، فقد استعملنا التعليمة extends للوراثة من الملفّ base.html الذي يُعتبر قاعدة الصّفحة، بعدها حدّدنا عنوانا للصّفحة عن طريق السّطر {% block title %} My Application {% endblock %}، بعدها حدّدنا جسم الصّفحة داخل الجزء body ومن ثمّ استعملنا التعليمة include لتضمين كلّ من الملفّات header.html، content.html وfooter.html داخل الصّفحة، لكن لاحظ بأنّ التّرتيب مُهمّ وهو نفس التّرتيب المنطقي الذي يجب اتّباعه، بحيث يكون تضمين الجزء العلوي أولا ثمّ الجزء المسؤول عن المُحتوى ثمّ الجزء السّفلي للصّفحة. يُمكنك أن تتخيّل بأنّ الجملة include غير مُتواجدة ويتواجد في مكانها كلّ الشّيفرة المتواجدة داخل ملفّ HTML المُضمّن، فعوضا عن السّطر {% include 'header.html' %} ستكون شيفرة الملفّ header.html وهكذا… بعد إنهاء تطبيق ما سبق، ستُلاحظ نتيجة مُشابهة لما في الصّورة التّاليّة: متى تستعمل التعليمة include ولماذا استعمال التعليمة include مُفيد لأنّه يُعطيك حرّية أكثر في العمل على أجزاء مُختلفة من صفحات الويب في تطبيقك، إذ يُمكنك تطوير الجزء السّفلي من الصّفحة دون الاضطرار إلى المرور بالشّفرة المسؤولة عن الأجزاء الأخرى، ما يُتيح لك تجربة تطوير أفضل، بالإضافة إلى أنّ تقسيم أجزاء الصّفحات إلى ملفّات HTML مُستقلّة أمر مُفيد إن كنت تعمل مع أحدهم على تطوير نفس التّطبيق أو حتّى فريق مُكوّن من أكثر من شخصين، إذ يُمكنك مثلا العمل على الجزء العلوي من الصّفحة وتترك صديقك يعمل على مُحتويات الصّفحة وصديق آخر يعمل على الجزء السّفلي بحيث لا يُعدّل الجميع نفس الملفّ في نفس الوقت. بالإضافة إلى أنّ التعليمة include تُساعد على تقسيم ملفّ HTML إلى عدّة ملفّات صغيرة لتسهيل عمليّة التّطوير، يُمكن كذلك أن تستفيد من هذه الملفّات عبر إعادة استعمالها في أكثر من ملفّ HTML لتفادي تكرار الشّيفرة واختصار الوقت. خاتمة تعرّفنا في هذا الدّرس على كيفيّة تقسيم صفحة HTML إلى عدّة ملفّات وكيفيّة جمعها داخل ملفّ واحد باستعمال الجملة include لتوفير تجربة تطوير أفضل، في الدّرس القادم، سنلقي نظرة على مبدأ الاستيرادات في مُحرّك القوالب Jinja وكيفيّة الاستفادة منه.
  9. تمهيد الدالة str.format()‎ المتوافرة للسلاسل النصية تسمح لك باستبدال المتغيرات وتنسيق القيم. مما يمنحك القدرة على تجميع العناصر مع بعضها عبر إدخالها في مواضع معينة. سيشرح لك هذا الدرس أشهر الاستخدامات لآلية تنسيق السلاسل النصية في بايثون، والتي ستساعدك في جعل شيفرتك وبرنامجك أسهل قراءةً واستخدامًا. استخدام «المُنسِّقات» تعمل المُنسِّقات (formatters) بوضع حقول قابلة للاستبدال تُعرَّف عبر وضع قوسين معقوفين {} في السلسلة النصية ثم استدعاء الدالة str.format()‎، إذ ستُمرَّر القيمة التي تريد وضعها ضمن السلسلة النصية إلى الدالة format()‎ وستوضع هذه القيمة في نفس مكان الحقل القابل للاستبدال الموجود في السلسلة الأصلية عندما تُشغِّل برنامجك. لنطبع سلسلةً نصيةً تستخدم «مُنسِّقًا» (formatter): print("Sammy has {} balloons.".format(5)) الناتج: Sammy has 5 balloons. أنشأنا في المثال السابق سلسلةً نصيةً تحتوي على قوسين معقوفين: "Sammy has {} balloons." ثم أضفنا الدالة str.format()‎ ومررنا إليها القيمة الرقمية 5 وهذا يعني أنَّ القيمة 5 ستوضع مكان القوسين المعقوفين: Sammy has 5 balloons. يمكننا أيضًا إسناد السلسلة النصية الأصلية التي تحتوي مُنسِّقًا إلى متغير: open_string = "Sammy loves {}." print(open_string.format("open source")) الناتج: Sammy loves open source. أضفنا في المثال السابق السلسلة النصية "open source" إلى سلسلةٍ نصيةٍ أكبر باستبدالها للقوسين المعقوفين الموجودَين في السلسلة الأصلية. تسمح لك المُنسِّقات في بايثون باستخدام الأقواس المعقوفة لحجز أماكن للقيم التي ستمررها مستقبلًا عبر الدالة str.format()‎. استخدام المُنسِّقات لحجز أكثر من مكان يمكنك استخدام أكثر من زوج من الأقواس المعقوفة عند استعمال المُنسِّقات؛ فيمكنك أن تضيف سلسلةً نصيةً أخرى إلى المثال السابق وذلك بإضافة زوج آخر من الأقواس المعقوفة وتمرير قيمة ثانية إلى الدالة كما يلي: new_open_string = "Sammy loves {} {}." # {} مكانين محجوزين عبر print(new_open_string.format("open-source", "software")) # تمرير قيمتين إلى الدالة مفصولٌ بينهما بفاصلة الناتج: Sammy loves open-source software. أضفنا زوجًا آخر من الأقواس المعقوفة إلى السلسلة النصية للسماح بوضع قيمة ثانية، ثم مررنا سلسلتين نصيتين إلى الدالة str.format()‎ مفصولٌ بينهما بفاصلة. سنضيف عمليات استبدال أخرى عبر اتباع نفس الآلية التي شرحناها أعلاه: sammy_string = "Sammy loves {} {}, and has {} {}." print(sammy_string.format("open-source", "software", 5, "balloons")) الناتج: Sammy loves open-source software, and has 5 balloons. إعادة ترتيب المنسقات عبر المعاملات الموضعية عندما نترك الأقواس المعقوفة دون معاملات (parameters) ممررة إليها، فستضع بايثون القيم المُمرَّرة إلى الدالة str.format()‎ بالترتيب. هذا تعبيرٌ فيه زوجين من الأقواس المعقوفة يوضع مكانهما سلسلتان نصيتان شبيهٌ بما رأيناه سابقًا في هذا الدرس: print("Sammy the {} has a pet {}!".format("shark", "pilot fish")) الناتج: Sammy the shark has a pet pilot fish! اُستبدِل أوّل زوجٍ من الأقواس المعقوفة ووضعت مكانه القيمة "shark"، ووضعت القيمة "pilot fish" مكان الزوج الثاني من الأقواس. القيم التي مررناها إلى الدالة str.format()‎ كانت بهذا الترتيب: ("shark", "pilot fish") إن سبق لك دراسة أنواع البيانات المُختلفة على بايثون فقد تلاحظ أنَّ القيمة السابقة هي من النوع tuple، ويمكن الوصول إلى كل قيمة موجودة فيها عبر فهرسٍ رقميٍ تابعٍ لها، والذي يبدأ من الفهرس 0. يمكننا تمرير أرقام الفهارس إلى داخل القوسين المعقوفين: print("Sammy the {0} has a pet {1}!".format("shark", "pilot fish")) سنحصل بعد تنفيذ المثال السابق على نفس الناتج التي ظهر دون تحديد أرقام الفهارس يدويًا، وذلك لأننا استدعينا القيم بالترتيب: Sammy the shark has a pet pilot fish! لكن إن عكسنا أرقام الفهارس في معاملات الأقواس المعقوفة فسنتمكن من عكس ترتيب القيم المُمرَّرة إلى السلسلة النصية الأصلية: print("Sammy the {1} has a pet {0}!".format("shark", "pilot fish")) الناتج: Sammy the pilot fish has a pet shark! لكن إن حاولت استخدام الفهرس ذي الرقم 2 ولم تكن لديك إلا قيمتين موجودتين في الفهرسين 0 و 1، فأنت تستدعي قيمةً خارج المجال المسموح، ولهذا السبب ستظهر رسالة خطأ: print("Sammy the {2} has a pet {1}!".format("shark", "pilot fish")) الناتج: IndexError: tuple index out of range تُشير رسالة الخطأ إلى وجود قيمتين فقط ومكانهما هو 0 و1، لذا كان الفهرس 2 غير مرتبطٍ بقيمةٍ وكان خارج المجال المسموح. لنضف الآن مكانين محجوزين إلى السلسلة النصية ولنمرر بضع قيم إلى الدالة str.format()‎ لكي نفهم آلية إعادة الترتيب فهمًا تامًا. هذه هي السلسلة النصية الجديدة التي فيها أربعة أزواج من الأقواس المعقوفة: print("Sammy is a {}, {}, and {} {}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a happy, smiling and blue shark! ستوضع القيم المُمرَّرة إلى الدالة str.format()‎ بنفس ترتيب ورودها في حال لم نستعمل المعاملات داخل الأقواس المعقوفة. تملك السلاسل النصية المُمرَّرة إلى الدالة str.format()‎ الفهارس الآتية المرتبطة بها: “happy” “smiling” “blue” “shark” 0 1 2 3 لنستخدم الآن أرقام الفهارس لتغيير ترتيب ظهور القيم المرتبطة بها في السلسلة النصية: print("Sammy is a {3}, {2}, and {1} {0}!".format("happy", "smiling", "blue", "shark")) الناتج: Sammy is a shark, blue, and smiling happy! ولمّا كنّا قد بدأنا بالفهرس ذي الرقم 3، فستظهر القيمة "shark" أولًا. أي أنَّ وضع رقم الفهرس بين القوسين كمعامل سيؤدي إلى تغيير ترتيب ظهور القيم في السلسلة النصية الأصلية. نستطيع -بالإضافة إلى المعاملات الموضعية الرقمية- أن نربط بين القيم وبين كلمات محجوزة مخصصة ومن ثم نستدعيها عبر وضع الكلمة المحجوزة بين القوسين المعقوفين كما يلي: print("Sammy the {0} {1} a {pr}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the shark made a pull request. أظهر المثال السابق استخدام كلمة محجوزة كوسيط بالإضافة إلى المعاملات الموضعية، يمكنك استخدام الكلمة المحجوزة pr كوسيط بالإضافة إلى أرقام الفهارس، وتستطيع أيضًا إعادة ترتيب تلك الوسائط كيفما شئت: print("Sammy the {pr} {1} a {0}.".format("shark", "made", pr = "pull request")) الناتج: Sammy the pull request made a shark. استخدام المعاملات الموضعية والكلمات المحجوزة سيمنحنا تحكمًا أكبر بكيفية معالجة السلسلة النصية الأصلية عبر إعادة ترتيب القيم المُمرَّرة إليها. تحديد نوع القيمة يمكنك وضع معاملات أخرى ضمن القوسين المعقوفين، سنستخدم الصيغة الآتية {field_name:conversion} حيث field_name هو الفهرس الرقمي للوسيط الممرَّر إلى الدالة str.format()‎ والذي شرحناه تفصيليًا في القسم السابق، و conversion هو الرمز المستعمل للتحويل إلى نوع البيانات الذي تريده. «رمز التحويل» يعني رمزًا من حرفٍ وحيد الذي تستخدمه بايثون لمعرفة نوع القيمة المُراد «تنسيقها». الرموز التي سنستخدمها في أمثلتنا هي s للسلاسل النصية و d لإظهار الأرقام بنظام العد العشري (ذي الأساس 10) و f لإظهار الأعداد ذات الفاصلة. يمكنك قراءة المزيد من التفاصيل عن رموز التنسيق في بايثون 3 (وغير ذلك من المواضيع المرتبطة بهذا المجال) في التوثيق الرسمي. لننظر إلى مثالٍ نُمرِّر فيه رقمًا صحيحًا عبر الدالة format()‎ لكننا نريد إظهاره كعددٍ ذي فاصلة عبر رمز التحويل f: print("Sammy ate {0:f} percent of a {1}!".format(75, "pizza")) الناتج: Sammy ate 75.000000 percent of a pizza! وضعت القيمة -مكان أوّل ورود للصيغة {field_name:conversion}- كعددٍ ذي فاصلة، أما ثاني ورود للقوسين المعقوفين فكان بالصيغة {field_name}. لاحظ في المثال السابق وجود عدد كبير من الأرقام الظاهرة بعد الفاصلة العشرية، لكنك تستطيع تقليل عددها. فعندما نستخدم الرمز f للقيم ذات الفاصلة نستطيع أيضًا تحديد دقة القيمة الناتجة بتضمين رمز النقطة . متبوعًا بعدد الأرقام بعد الفاصلة التي نود عرضها. حتى لو أكل سامي 75.765367% من قطعة البيتزا فلن نحتاج إلى هذا القدر الكبير من الدقة، إذ يمكننا مثلًا أن نجعل عدد المنازل العشرية ثلاث منازل بعد الفاصلة بوضعنا ‎.3 قبل رمز التحويل f: print("Sammy ate {0:.3f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.765 percent of a pizza! أما إذا أردنا عرض منزلة عشرية وحيدة، فيمكننا إعادة كتابة السلسلة السابقة كالآتي: print("Sammy ate {0:.1f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 75.8 percent of a pizza! لاحظ كيف أدى تعديل دقة الأرقام العشرية إلى تقريب الرقم (وفق قواعد التقريب الاعتيادية). وصحيحٌ أننا عرضنا رقمًا دون منازل عشرية كعددٍ ذي فاصلة، إلا أننا إذا حاولنا تحويل عدد عشري إلى عدد صحيح باستخدام رمز التحويل d فسنحصل على خطأ: print("Sammy ate {0:.d} percent of a pizza!".format(75.765367)) الناتج: ValueError: Unknown format code 'd' for object of type 'float' إذا لم ترغب بعرض أيّة منازل عشرية، فيمكنك كتابة تعبير كالآتي: print("Sammy ate {0:.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! لن يؤدي ما سبق إلى تحويل العدد العشري إلى عددٍ صحيح، وإنما سيؤدي إلى تقليل عدد المنازل العشرية الظاهرة بعد الفاصلة. إضافة حواشي لمّا كانت الأماكن المحجوزة عبر القوسين المعقوفين هي حقول قابلة للاستبدال (أي ليست قيمًا فعليةً) فيمكنك إضافة حاشية (padding) أو إضافة فراغ حول العنصر بزيادة حجم الحقل عبر معاملات إضافية، قد تستفيد من هذا الأمر عندما تحتاج إلى تنظيم البيانات بصريًا. يمكننا إضافة حقلٍ بحجمٍ معيّن (مُقاسًا بعدد المحارف) بتحديد ذاك الحجم بعد النقطتين الرأسيتين : كما في المثال الآتي: print("Sammy has {0:4} red {1:16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! أعطينا في المثال السابق حقلًا بحجم 4 محارف للعدد 5، وأعطينا حقلًا بحجم 16 محرفًا للسلسلة النصية balloons (لأنها سلسلة طويلة نسبيًا). وكما رأينا من ناتج المثال السابق، يتم محاذاة السلاسل النصية افتراضيًا إلى اليسار والأعداد إلى اليمين، يمكنك أن تُغيّر من هذا بوضع رمز خاص للمحاذاة بعد النقطتين الرأسيتين مباشرةً. إذ سيؤدي الرمز > إلى محاذاة النص إلى يسار الحقل، أما الرمز ^ فسيوسِّط النص في الحقل، والرمز < سيؤدي إلى محاذاته إلى اليمين. لنجعل محاذاة العدد إلى اليسار ونوسِّط السلسلة النصية: print("Sammy has {0:<4} red {1:^16}!".format(5, "balloons")) الناتج: Sammy has 5 red balloons ! نلاحظ الآن أنَّ محاذاة العدد 5 إلى اليسار، مما يعطي مساحة فارغةً في الحقل قبل الكلمة red، وستظهر السلسلة النصية balloons في منتصف الحقل وتوجد مسافة فارغة على يمينها ويسارها. عندما نُنسِّق الحقل لنجعله أكبر من حجمه الطبيعي فستملأ بايثون الحقل افتراضيًا بالفراغات، إلا أننا نستطيع تغيير محرف الملء إلى محرفٍ آخر بوضعه مباشرةً بعد النقطتين الرأسيتين: print("{:*^20s}".format("Sammy")) الناتج: *******Sammy******** ستضع بايثون السلسلة النصية المُمرَّرة إلى الدالة str.format()‎ ذات الفهرس 0 مكان القوسين المعقوفين لأننا لم نطلب منها عكس ذلك، ومن ثم سنضع النقطتين الرأسيتين ثم سنُحدِّد أننا سنستعمل المحرف * بدلًا من الفراغات لملء الحقل، ثم سنوسِّط السلسلة النصية عبر استعمال الرمز ^ مُحدِّدين أنَّ حجم الحقل هو 20 محرف، ومُشيرين في نفس الوقت إلى أنَّ الحقل هو حقلٌ نصيٌ عبر وضع الرمز s. يمكننا استخدام هذه المعاملات مع المعاملات التي استخدمناها وشرحناها في الأقسام السابقة: print("Sammy ate {0:5.0f} percent of a pizza!".format(75.765367)) الناتج: Sammy ate 76 percent of a pizza! حدِّدنا داخل القوسين المعقوفين رقم فهرس القيمة العددية ثم وضعنا النقطتين الرأسيتين ثم اخترنا حجم الحقل ثم وضعنا نقطةً . لنضبط عدد المنازل العشرية الظاهرة، ثم اخترنا نوع الحقل عبر رمز التحويل f. استخدام المتغيرات مرَّرنا منذ بداية هذا الدرس وإلى الآن الأعداد والسلاسل النصية إلى الدالة str.format()‎ مباشرةً، لكننا نستطيع تمرير المتغيرات أيضًا، وذلك بنفس الآلية المعتادة: nBalloons = 8 print("Sammy has {} balloons today!".format(nBalloons)) الناتج: Sammy has 8 balloons today! يمكننا أيضًا استخدام المتغيرات لتخزين السلسلة النصية الأصلية بالإضافة إلى القيم التي ستُمرَّر إلى الدالة: sammy = "Sammy has {} balloons today!" nBalloons = 8 print(sammy.format(nBalloons)) الناتج: Sammy has 8 balloons today! ستُسهِّل المتغيرات من التعامل مع تعبيرات التنسيق وتُبسِّط عملية إسناد مدخلات المستخدم وإظهارها مُنسَّقةً في السلسلة النصية النهائية. استخدام المُنسِّقات لتنظيم البيانات يسطع نجم آلية التنسيق التي نشرحها في هذا الدرس عندما تُستخدَم لتنظيم البيانات بصريًا، فلو أردنا إظهار نتائج قاعدة البيانات إلى المستخدمين، فيمكننا استعمال المُنسِّقات لزيادة حجم الحقل وتعديل المحاذاة لجعل الناتج أسهل قراءةً. لننظر إلى حلقة تكرار تقليدية في بايثون التي تطبع i و i*i و i*i*i لمجالٍ من الأعداد من 3 إلى 13: for i in range(3,13): print(i, i*i, i*i*i) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 صحيحٌ أنَّ الناتج مُنظَّمٌ قليلًا، إلا أنَّ الأعداد تتداخل مع بعضها بصريًا مما يُصعِّب قراءة الأسطر الأخيرة من الناتج، وإذا كنتَ تتعامل مع مجموعة أكبر من البيانات التي يتواجد فيها أعداد أكبر (أو أصغر) مما عرضناه في مثالنا، فقد تبدو لك المشكلة جليةً حينها. لنحاول تنسيق الناتج السابق لإعطاء مساحة أكبر لإظهار الأعداد: for i in range(3,13): print("{:3d} {:4d} {:5d}".format(i, i*i, i*i*i)) لم نُحدِّد في المثال السابق ترتيب الحقل وبدأنا مباشرةً بكتابة النقطتين الرأسيتين متبوعةً بحجم الحقل ورمز التحويل d (لأننا نتعامل مع أعداد صحيحة). أعطينا في المثال السابق حجمًا للحقل مساويًا لعدد أرقام العدد الذي نتوقع طباعته في الحقل المعني مضافًا إليه 2، لذا سيبدو الناتج كالآتي: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تحديد حجم ثابت للحقل لنحصل على أعمدة متساوية العرض، مما يضمن إظهار الأعداد الكبيرة بصورة صحيحة: for i in range(3,13): print("{:6d} {:6d} {:6d}".format(i, i*i, i*i*i)) الناتج: 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 11 121 1331 12 144 1728 يمكننا أيضًا تعديل محاذاة النص الموجود في الأعمدة باستخدام الرموز > و ^ و <، وتبديل d إلى f لإظهار منازل عشرية، وغير ذلك مما تعلمناه في هذا الدرس لإظهار البيانات الناتجة كما نرغب. الخلاصة قد يكون استخدام المنسقات لوضع القيم في سلسلةٍ نصيةٍ أمرًا مفيدًا لتسهيل تنظيم البيانات والقيم، فالمنسقات هي آليةٌ تساعد في جعل الناتج مقروءًا للمستخدم. ترجمة -وبتصرّف- للمقال How To Use String Formatters in Python 3 لصاحبته Lisa Tagliaferri.
  10. تمهيد إلى الآن، تعرّفنا على العديد من أساسيّات تطوير الويب باستعمال كلّ من إطار العمل Flask ومُحرّك القوالب Jinja، وفي الدّرس السّابق، تعرّفنا على ماهيّة الدّوال المُسبقة التّعريف في هذا الأخير، إذ تعرّفنا كبداية على كلّ من الدّالة range() والدّالة lipsum، وسنُكمل في هذا الدّرس ما بدأناه لنتعرّف على بقيّة الدّوال والأصناف المبنيّة مُسبقا في مُحرّك القوالب Jinja ليُساعدك ذلك على تطوير تطبيقات وِب أفضل. الدّالة dict في لغة بايثون، هناك نوع خاصّ من القيم يُسمّى القواميس Dictinaries، في كلّ قاموس نجد مفاتيح وقيّما لتُساعدنا على ترتيب البيانات وتسهيل البحث عن قيمة ما عبر مفتاحها، كما نستطيع الوصول إلى كلّ من القيم والمفاتيح باستعمال حلقة for بسيطة. في مُحرّك القوالب Jinja، القواميس مُشابهة لقواميس بايثون، إذ يُمكنك تعريف قاموس كما يلي: {% set dictionary = {'1': 'One', '2': 'Two'} %} في المثال أعلاه، كلّ من 1 و 2 عبارة عن مفاتيح، أمّا One و Two فهي قيم المفاتيح، بحيث One قيمة المفتاح 1 و Two قيمة المفتاح 2. للحصول على القيم في القاموس، يُمكن استخدام اسم المُتغيّر الذي يحمل القاموس مع المفاتيح كما يلي: {{ dictionary['1'] }} <br> {{ dictionary['2'] }} النّتيجة: One Two مُلاحظة: في المثال أعلاه، قُمنا باستخدام الكلمة المفتاحيّة set لتعريف مُتغيّر يحمل قاموسا كقيمة، لكن يُمكن كذلك أن تُمرّر مُتغيّرا يحمل قاموسا من ملفّات التّحكم – وهو الجزء الّذي يهتمّ به إطار العمل Flask - إلى قوالب HTML، لذا ففي هذه الحالة ستتمكّن من الوصول إلى القاموس مُباشرة من المُتغيّر دون تعريفه على نحو صريح باستعمال مُحرّك القوالب Jinja. يُمكنك في مُحرّك القوالب Jinja إنشاء قواميس بطريقة أفضل عبر الاستعانة بالدّالة dict()، إذ يُمكنك استعمال الدّالة لإنشاء قاموس تكون مفاتيحه هي أسماء المُعاملات وقيمة المُعامل هي نفسها قيمة المفتاح. التّالي مثال على كيفيّة استخدام الدّالة dict() لإنشاء قاموس بسيط: dict(key='value', another_key='another value') المثال أعلاه سيُنتج قاموسا كما يلي: {'another_key': 'another value', 'key': 'value'} تُستخدم القواميس عندما يكون لدينا نوع من القيم المُترابطة والتّي لها أصل واحد، فمثلا لنقل بأنّ القاموس يُمثّل بطاقة تعريف أحدهم، في القاموس سيتوجّب علينا تخزين الاسم الأوّل للشّخص، والاسم العائلي أو نسبُ نفس الشّخص. المثال التّالي توضيح على كيفيّة استخدام الدّالة dict لإنشاء قاموس يعمل على تخزين الاسم الكامل لشخص ما وعرض جملة ترحيبيّة مُخصّصة له: {% set dictionary = dict(firstname='أحمد', lastname='علي') %} مرحبا يا {{ dictionary['firstname'] }} {{ dictionary['lastname'] }}! النّتيجة ستكون الجملة التّاليّة: مرحبا يا أحمد علي! وبالطّبع فالنّتيجة ستتغيّر كلّما غيّرت أحد قيمتي المُعاملين firstname وlastname أثناء استدعاء الدّالة dict. من الأمور المُميّزة في الدّالة dict أنّها تُعطيك الحرّية في اختيار كيفيّة الوصول إلى قيمة كلّ مفتاح. سابقا، استعملنا الطّريقة التّقليديّة للوصول إلى قيم القاموس كما يلي: dictionary['firstname'] dictionary['lastname'] لكن يُمكننا كذلك الوصول إلى القيم بطريقة أسرع كما يلي: dictionary.firstname dictionary.lastname بهذه الطّريقة يُمكنك اعتبار المفاتيح خصائص كما في خصائص الأصناف في لغة بايثون، ما يعني بأنّك تستطيع استخدام المُرشّحات والاختبارات التّي تسمح لك باختبار قيم الخصائص مُباشرة. بالتّالي فالمثال السّابق سيكون كما يلي: مرحبا يا {{ dictionary.firstname }} {{ dictionary.lastname }}! ستُلاحظ بأنّ النّتيجة هي نفسها وبأنّ هذا المثال الأخير أفضل من المثال السّابق لأنّك تستطيع اختصار بعض الوقت عند الوصول إلى قيم المفاتيح بهذه الطّريقة. الصّنف cycler يُمكّنك الصّنف cycler من الدّوران حول مجموعة من العناصر بشكل لا نهائي، مع إمكانيّة إعادة الدّوران في وقت مُحدّد إن أردت ذلك. على سبيل المثال، لنقل بأنّك تُريد الدّوران حول قائمة من الأسماء بشكل لا نهائي بحيث تُعاد الدّورة في كلّ مرّة. لنضع الشّيفرة التّالية كمثال لكيفيّة استعمال الصّنف cycler: {% set names = cycler('Ahmed', 'Ali', 'Hassan') %} في هذا المثال، أنشأنا كائنا من الصّنف cycler وأسندناه إلى المُتغيّر names. يُمكننا الآن استخدام الكائن names للدّوران حول قائمة الأسماء التّي مرّرناها إلى الصّنف cycler. من المُهم أن نفهم بأنّ الكائن names سيحمل قيمة واحدة فقط في كلّ مرّة نستدعيه فيه باستخدام أحد توابع أو خصائص الصّنف cycler، إذ يُمكننا استخدام الخاصيّة current للحصول على القيمة الحاليّة، والتّابع next() للوصول إلى القيمة التّاليّة، بالإضافة إلى التّابع reset() لإعادة الدّوران من جديد والوصول إلى أول قيمة. القيمة التي سيحملها الكائن names عند استعمال أحد التّوابع ستكون أحد الأسماء في مجموعة الأسماء التّي حدّدناها من قبل فقط ولا يُمكن أن تكون قيمة أخرى. الوصول إلى قيمة الدّوران الحاليّة إن اعتبرنا المثال السّابق، فإنّنا نستطيع الوصول إلى قيمة الدّوران الحاليّة كما يلي: {% set names = cycler('Ahmed', 'Ali', 'Hassan') %} {{ names.current }} وبما أنّنا لم نقم بأي انتقال من قيمة إلى الّتي تليها عبر التّابع next()، فالقيمة البدئيّة ستكون أول عنصر من مجموعة العناصر المُمرّرة إلى الصّنف cycler، أي الاسم Ahmed في هذه الحالة. الوصول إلى قيمة الدّوران التّاليّة للوصول إلى القيمة التّاليّة وعرضها، يُمكنك استخدام التّابع next() على الكائن names كما يلي: {{ names.next() }} ستُلاحظ بأنّ النّتيجة هي نفسها النّتيجة التّي تظهر عند استخدام الخاصيّة current أي أنّ القيمة التّي ظهرت هي العنصر الأول من قائمة العناصر. هذا لأنّه يتوجّب علينا استخدام التّابع next() أكثر من مرّة ليظهر مفعوله: {{ names.next() }} <br> {{ names.next() }} <br> {{ names.next() }} <br> {{ names.next() }} <br> {{ names.next() }} <br> استخدمنا في المثال أعلاه التّابع next() خمس مرّات، والنّتيجة كما المُتوقّع ستكون كالتّالي: Ahmed Ali Hassan Ahmed Ali كما تُلاحظ، عندما وصلت القيمة إلى آخر عنصر (أي الاسم Hassan)، فقد عادت الدّورة إلى البداية من جديد لتستمرّ في الدّوران حول القيم، هذا يعني بأنّك تستطيع استخدام التّابع next() لعدد لا نهائي من المرّات مع أي مجموعة من العناصر. استخدام الصّنف cycler مع قائمة بايثون بما أنّ الصّنف cycler يعمل مع قيمٍ عدّة، فمن الطّبيعي أن ترغب في استخدامه مع قائمة عاديّة تحتوي على مجموعة من العناصر. لكن إن مرّرت قائمة إلى الصّنف cylcer كما يلي: set names = cycler(['Ahmed', 'Ali', 'Hassan']) فسيعتبر الصّنف هذه القائمة المُمرّرة مُجرّد قيمة واحدة، ولو استعملت مثلا التّابع next() على القائمة مرّتين فستحصل على النّتيجة التّالية: ['Ahmed', 'Ali', 'Hassan'] ['Ahmed', 'Ali', 'Hassan'] هذا يعني بأنّ الصّنف cycler لا يعتبر القائمة مجموعة من العناصر كما يجب. لحلّ هذه المُشكلة، استعمل الرّمز * قبل القائمة لتفكيكها وتمرير كلّ عنصر منها على شكل مُعامل كما يلي: set names = cycler(*['Ahmed', 'Ali', 'Hassan']) هذه الطّريقة تعمل حتى لو كانت القائمة مُسندة إلى مُتغيّر ما كذلك، على سبيل المثال إن كانت لديك قائمة باسم list وأردت تمرير عناصرها إلى الصّنف cycler فيُمكنك ذلك كما يلي: cycler(*list) خاتمة تعرّفنا في هذا الدّرس على كلّ من الدّالة dict والصّنف cycler لمُساعدتك على بناء صفحات HTML أفضل عند استعمال كلّ من إطار العمل Flask ومُحرّك القوالب Jinja، في الدّرس القادم سنتعرّف على جزء آخر لا يقلّ أهميّة عمّا درسناه في الدّروس السّابقة، ألا وهو كيفيّة التّعامل مع أكثر من ملفّ عبر تضمينها بعضها ببعض لتفادي تكرار الشّيفرة البرمجيّة.
  11. مُقدّمة بعد أن تعرّفنا على المُرشّحات والاختبارات في مُحرّك القوالب Jinja، بقي أن نتعرّف على الدّوال التّي يُمكنك استعمالها لتسهيل عمليّة تطوير قوالب HTML، هذه الدّوال مُشابهة للدّوال التّي يُمكنك استخدامها في لغة بايثون، ما يعني بأنّك تستطيع استخدامها كما تُستخدم في لغة بايثون، أي باستدعائها وتمرير مُعاملات إليها إن كان ذلك مُمكنا. في هذا الدّرس، سنتعرّف على كلّ من الدّالة range والدّالة lipsum اللّتان تُعتبران دالّتين مُهمّتين لكل مُصمّم قوالب ولمُطوّري الويب الّذين يستخدمون إطار العمل Flask خصّيصا. ولتطبيق الأمثلة المذكورة في هذا الدّرس، يُمكنك الاستعانة بملفّ filters.html الّذي أنشأناه سابقا في بداية هذه السّلسة للتّحقق من نتائج أمثلة المُرشّحات. إن لم تتّبع سلسلة الدّروس منذ البداية، فلا مُشكلة، إذ يُمكنك إنشاء تطبيق بسيط بإطار العمل Flask بحيث يحتوي على موجّه واحد على الأقل يُقدّم صفحة HTML لتستطيع التّعامل معها عبر مُحرّك القوالب Jinja الّذي يُعتبر الأداة الافتراضيّة لإدارة قوالب HTML في تطبيقات الويب المبنيّة بإطار العمل Flask. بعد تجهيز قالب لنختبر به الأمثلة، لننتقل الآن إلى التّعرف على الدّوال المبنيّة مُسبقا في مُحرّك القوالب Jinja. الدّالة range الدّالة range() في Jinja تعمل بشكل مُشابه لطريقة عمل الدّالة التّي تحمل نفس الاسم في لغة Python. إذ تُساعدك على إنشاء قائمة أعداد حسب المُعاملات. افتراضيّا، الدّالة range تعمل إذا ما مرّرت إليها قيمة واحدة كمُعامل عددي، والنّتيجة تكون قائمة بالأعداد من 0 إلى العدد ما قبل العدد الذي مُرِّرَ كمُعامل. مثلا، استدعاء الدّالة range مع تمرير العدد 10 كمُعامل سيكون كما يلي: range(10) نتيجة المثال ستكون كما يلي: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] لاحظ بأنّ النّتيجة عبارة عن قائمة تحتوي على 10 عناصر من الصّفر إلى تسعة دون تضمين العدد 10 الذي يُعتبر المُعامل المُستخدم. مُلاحظة: إن طبّقت المثال أعلاه في ملفّ HTML فلن تظهر النّتيجة حتّى تُطبّق المُرشّح list كما يلي: {{ range(10) | list }} لكن عموما تستطيع استخدام حلقة for مع نتيجة الدّالة مُباشرة دون استخدام المُرشّح list. تحديد القيمة البدئيّة عندما نستعمل الدّالة range في ملفّات HTML مع تمرير مُعامل واحد فقط، فالقيمة البدئيّة تكون دائما العدد 0، لكن يُمكنك تغيير هذا الوضع بتمرير مُعامل قبل مُعامل القيمة القُصوى ليبدأ العدّ من هذه القيمة الجديدة إلى حين الوصول إلى العدد ما قبل القيمة القُصوى. كمثال على ذلك، لنعتبر بأنّنا نحتاج إلى قائمة تبدأ من العدد 5 وتنتهي بالعدد 15، في هذه الحالة سيتوجّب علينا استخدام الدّالة range كما يلي: {{ range(5, 15) | list }} هكذا ستكون النّتيجة كما يلي: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14] كما تُلاحظ، فقد انتهت القائمة بالعدد 14 وليس بالعدد 15 كما قد تتوقّع، لذا لو أردت تضمين العدد 15 في القائمة فسيتوجّب عليك استخدام العدد 16 في المقام الأول كما يلي: range(5, 16) النّتيجة: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] تحديد قيمة الزّيادة افتراضيّا، قيمة الزّيادة هي العدد 1، إذ يكون كلّ عنصر أكبر من العدد الذّي يسبقه في القائمة بواحد، وإن أردت أن تُغيّر قيمة الزّيادة فيُمكنك تمريرها كمُعامل ثالث. على سبيل المثال، لو أردنا الحصول على العشرات التّي تحتوي على رقمين فقط (20, 50 …) فسيُمكننا أن نقوم بتوليد قائمة عناصرها تبدأ من العدد 10 إلى العدد 100 بزيادة قيمتها العدد 10، ويُمكننا القيام بالأمر كما يلي: range(10, 100, 10) النّتيجة ستكون جميع العشرات من 10 إلى 90 في قائمة كالتّالي: [10, 20, 30, 40, 50, 60, 70, 80, 90] يُمكن لقيمة الزّيادة أن تكون سلبيّة كذلك، ما يسمح لك بالحصول على قائمة عكسيّة أو عدّ تنازلي. فمثلا، يُمكننا الحصول على قائمة الأعداد من 10 إلى 1 كما يلي: range(10, 0, -1) هنا نضع العدد 10 كقيمة بدئيّة والعدد 0 كقيمة قُصوى لأنّ آخر عدد قبل الصّفر هو العدد 1، وفي قيمة الزّيادة نضع العدد السّلبي -1. النّتيجة: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] يُمكنك تغيير قيمة الزّيادة إلى أي عدد سلبي آخر حسب ظروف الاستعمال. أمثلة على استخدام الدّالة range في تطبيقات إطار العمل Flask بعد أن تعرّفنا على الهدف من الدّالة range وكيفيّة استعمالها في مُختلف الحالات مع عدّة مُعاملات، لننتقل الآن إلى أمثلة واقعيّة لكيفيّة استخدام هذه الدّالة في تطوير تطبيقات الويب. المثال الأول، تكرار نصّ ما عدّة مرّات في تطبيقات الويب، قد ترغب بتكرار نصّ ما عدّة مرّات لتوضيح فكرة أو للحصول على صورة عامّة للصّفحة مع مُحتوى وهمي. يُمكنك استخدام الدّالة range() لتكرار نصّ ما أو مقطع HTML عددا من المرّات حسب العدد المُمرّر كمُعامل، فمثلا، يُمكننا تكرار المقطع “مرحبا بالعالم!” 10 مرّات باستعمال الشّيفرة التّالية: {% for number in range(10) %} <p>مرحبا بالعالم!</p> {% endfor %} لاحظ بأنّنا قد تعاملنا مع الدّالة range كقائمة عاديّة دون الحاجة إلى استعمال المُرشّح list على نحو صريح. المثال الثّاني، توليد أرقام الصّفحات لنقل بأنّنا نمتلك العديد من السّجلات في قاعدة بياناتنا ونرغب بعرضها على شكل صَفحات، بالإضافة إلى أنّنا نريد توفير طريقة بسيطة لنُمكّن المُستخدم من التّنقل عبر الصّفحات بِحُرّيَة. يُمكننا في هذه الحالة أن نُنشئ شريطا لأرقام الصّفحات ونربط كلّ رقم برابط الصّفحة التّابعة له. التّالي مثال بسيط على كيفيّة إنشاء شريط تصفّح يُغطّي أرقام الصّفحات من 1 إلى 9: {% for number in range(10) %} <span> {{ number }} </span> {% endfor %} النّتيجة: 0 1 2 3 4 5 6 7 8 9 في المثال لم نقم سوى بعرض كل رقم داخل وسم <span> وإن أردت تطوير هذا المثال أكثر، فسيلزمك استخدام الدّالة url_for التي يوفّرها إطار العمل Flask لربط كلّ رقم بالصّفحة الخاصّة به. الدّالة lipsum إن كانت لك خلفيّة في التّصميم فلا بدّ أن تكون قد سمعت عن نصّ lorem ipsum، إن كان الأمر كذلك، فالدّالة lipsum() ستُساعدك على توليد مقاطع عشوائيّة من هذا النّص لتستعملها في ملفّات HTML الخاصّة بك. إن لم تسمع من قبل عن lorem ipsum، فهو ببساطة نصّ لاتيني يُستعمل في التّصاميم الطّباعيّة كمثال على نصّ صحيح، وذلك لكي تتوضّح الصّورة لدى المُصمّم ومن يرى التّصميم، وقد أصبح يُستعمل كثيرا في تصاميم الويب لتأديّة نفس الواجب، بحيث يُستعمل على شكل فقرات وعناوين في ملفّات HTML لتوضيح التّصميم والحصول على فكرة عن الصّورة العامّة للتّصميم عندما تكون النّصوص مُتوفّرة، فمثلا إن كنت تُصمّم صفحة ويب لعرض مقالات ما، فستنقصك المقالات لتتوضّح صورة التّصميم بالنّسبة إليك، لذا فاللجوء إلى توليد هذا النّص العشوائي أسرع طريقة للحصول على تصميم يحتوي على مقالات وهميّة. طريقة استعمال الدّالة هي كما يلي: {{ lipsum() }} افتراضيّا، تولّد الدّالة lipsum خمسة فقرات، كلّ فقرة تحتوي على ما بين 20 إلى 100 كلمة، ويُمكنك تغيير هذه الإعدادات عبر تمرير مُعاملات إلى الدّالة. لتغيير عدد الفقرات المُولّدة، يُمكنك تمرير العدد الذي ترغبه كمُعامل. لذا فإن كنت ترغب بالحصول على فقرتين فقط، فستحصل على هذه النّتيجة كالتّالي: {{ lipsum(2) }} الحدّ الأدنى لعدد الكلمات في كلّ فقرة هو 20 كلمة، إن كنت ترغب بتعديل هذا فمرّر العدد الأدنى كقيمة للمُعامل min. التّالي مثال على كيفيّة جعل الدّالة lipsum تولّد فقرة واحدة تحتوي على 60 كلمة أو أكثر دون النّزول عن هذا الحدّ: lipsum(1, min=60) الحدّ الأقصى لعدد الكلمات المُولّدة يكون 100 كلمة بشكل افتراضي، لكن تغييره يُمكنك تمرير العدد الّذي تودّ أن يكون العدد الأقصى إلى المُعامل max. على سبيل المثال، الشّيفرة التّاليّة ستقوم بتوليد فقرتين عدد كلمات كلّ واحدة منهما بين 50 إلى 80 كلمة: {{ lipsum(2, min=50, max=80) }} مثال يجمع كلّا من الدّالة range والدّالة lipsum بعد أن تعرّفنا على كلّ من الدّالة range والدّالة lipsum، حان الوقت لاستعمالهما في مثال واحد لتتّضح لك فكرة استعمالهما وتحصل على أفكار أخرى لكيفيّة استغلال ما تعلّمته في هذا الدّرس. في هذا المثال، سنقوم بعرض ثمانيّة فقرات من نصّ lorem ipsum مع عنوان يعمل على شكل رابط بالإضافة إلى زرّ لتمكين الزّائر من قراءة المزيد. وسنستعمل إطار العمل Bootstrap للحصول على تصميم جميل. شيفرة المثال ستكون كما يلي: {% for i in range(8) %} <div class='col-md-3'> <h2> <a href='#'> {{ lipsum(1, min=3, max=5) }} </a> </h2> {{ lipsum(1, max=50) }} <button class='btn btn-default'>Read more...</button> </div> {% endfor %} نقوم أولا باستعمال حلقة for مع الدّالة range مع تمرير العدد 8 كمُعامل، لذا فأي شيفرة داخل حلقة for هذه ستتكرّر ثماني مرّات. داخل حلقة for، نستعمل وسم <div> وخاصيّة CSS col-md-3 التّي يُوفّرها لنا إطار العمل Bootstrap لتقسيم صفحة الويب لأجزاء مُتساويّة. داخل وسم <div> نستعمل الدّالة lipsum لتوليد نصّ عشوائي صغير ليظهر على شكل عنوان للمقال الوهمي، بعدها نستعمل الدّالة مُجدّدا لتوليد فقرة واحدة عدد كلماتها لا يتعدّى 50 كلمة، ثمّ بعد ذلك نعرض زرّا لقراءة المزيد. بعد تطبيق هذا المثال، سنحصل على نتيجة مُشابهة للصّورة التّاليّة: ختاماً تعرّفنا في هذا الدّرس على جزء آخر من أساسيّات مُحرّك القوالب Jinja، يُمكنك الآن استخدام كلّ من الدّالة range والدّالة lipsum لتطوير تطبيقات أفضل واختصار الكثير من الوقت عند العمل مع إطار العمل Flask، إضافة إلى أنّك ستستطيع استخدامها مع تطبيق إدارة المُحتوى “كلمة” والذي سنُكمل تطويره بعد إنهاء أساسيّات مُحرّك القوالب Jinja.
  12. مُقدّمة بعد أن تعرّفنا على ماهية ميّزة الاختبارات في مُحرّك القوالب Jinja، وتعرّفنا على بعض من أهمّ الاختبارات المبنيّة مُسبقا في Jinja والتّي يُمكنك استعمالها مُباشرة لتطوير قوالبHTML الخاصّة بك، قد لا تجد ما يسدّ حاجتك في الاختبارات التّي درسناها، وكما فعلنا مع المُرشّحات، يُمكننا سدّ هذه الحاجة بإنشاء اختبارات خاصّة بنا لاختبار القيم والتّحقق ممّا إذا كانت تتّبع نمطا مُعيّنا أو لا. مبدأ اختبارات Jinja المُخصّصة في إطار العمل Flask قبل الانتقال إلى أمثلة تطبيقيّة حول كيفيّة إنشاء اختبارات خاصّة بك لاستعمالها في قوالب Jinja مع إطار العمل Flask، سنتعرّف أولا على الشّكل المبدئي للاختبار. كما استعملنا المُزخرف template_filter لإنشاء مُرشّحات خاصّة بنا، فإنّنا نستعمل مُزخرفا آخر لإنشاء اختبار مُخصّص، إذ نستعمل المُزخرف template_test مع كائن التّطبيق app لتمكين جميع القوالب من استخدام الاختبار. شيفرة اختبار بسيط هي كالآتي: @app.template_test(name='test_name') def test_name_function(value): # Test value return True الشّيفرة شبيهة بشيفرة إنشاء مُرشّح مُخصّص، الفرق أنّنا نستعمل المُزخِرف template_test ونُمرّر اسم الاختبار إلى المُعامل name، ثمّ نمنح للدّالة المُزَخْرَفَةِ اسما مُعبّرا، الدّالة تستقبل قيمة افتراضيّة (القيمة المُختبَرَة) تتواجد داخل المُعامل value، داخل الدّالة ستقوم بإجراءات لاختبار القيمة value مكان التّعليق Test value ثمّ تُرجع إمّا القيمة المنطقيّة True أو القيمة المنطقيّة False حسب ما إذا اجتازت القيمة الاختبار أو لا. طريقة استعمال الاختبار الذي تقوم بإنشائه هي نفسُها طريقة استعمال الاختبارات المبنيّة مُسبقا: variable is test_name لاحظ بأنّ الاختبار يجب أن يُرجع قيمة منطقيّة، ولتبسيط الأمور، فالمثال يُرجع دائما القيمة المنطقيّة True. مثال على اختبار للتّحقق من أنّ طول قيمة ما لا يتجاوز حدا مُعيّنا في هذا المثال سنقوم بإنشاء اختبار للتّحقق ممّا إذا كانت سلسلة نصيّة مُعيّنة أكبر من 30 محرفا، الأمر الذي سيضمن لنا تنسيقا أفضل للصّفحات، فمثلا، لنقل بأنّك ترغب بأن تكون عناوين المقالات في تطبيقك قصيرة، ولنقل بأنّك لا ترغب في عرض أي عنوان تزيد عدد أحرفه عن الثّلاثين حرفا لسبب أو لآخر. المثال التّالي يُوضّح كيفيّة إنشاء اختبار Jinja مُخصّص للتّحقّق من أنّ قيمة مُعيّنة أكبر من 30 محرفا: @app.template_test(name='long') def is_long_test(value): is_long = len(value) > 30 return is_long # True if value is more than 30 characters long نقوم أولا باستخدام المُزخرف app.template_test مع تمرير الاسم long إلى المعامل name وبالتالي سنتمكّن من استعمال الاختبار بالاسم long، في الدّالة المُزخرفة is_long_test نقوم بإنشاء مُتغيّر is_long يحمل إحدى القيمتين True أو False حسب ما إذا كان عدد أحرف السّلسلة النّصيّة value المُمرّرة افتراضيّا إلى الاختبار أكبر من 30 أو لا، للتّحقق ممّا إذا كانت كذلك، نستعمل المُعامل > (أكبر من) للمُقارنة بين طول القيمة value الذي نحصل عليه باستخدام الدّالة len والقيمة 30، في الأخير نُرجع قيمة المُتغيّر is_long. بعد إنشاء الاختبار في الملفّ project/__init__.py ستتمكّن من اختباره كما يلي: {{ 'Building A Web Application Using The Flask Framework' is long }} <br> {{ 'An Introduction To Flask' is long }} النّتيجة: True False في المثال اختبرنا قيمتين، الأولى عدد عناصرها أكبر من 30 محرفا، والقيمة الثّانية عبارة عن عنوان قصير لا يتعدّى الطّول الأقصى، لذا فمن الطّبيعي أن تكون نتيجة الاختبار الأول القيمة True لأنّ العنوان طويل بالفعل، ونتيجة اختبار القيمة الثّانية هي القيمة False لأنّ العنوان أقصر من الثّلاثين حرفا. يُمكنك كذلك ترك تحديد أكبر قيمة للمُطوّر عبر جعلها مُعاملا كما هو مُوضّح في المثال التّالي: @app.template_test(name='longer_than') def is_long_test(value, number_of_characters=30): is_long = len(value) > number_of_characters return is_long في هذا المثال أنشأنا اختبارا باسم longer_than، الدّالة المسؤولة عن الاختبار تقبل مُعاملا واحدا بالإضافة إلى القيمة value التّي تُمرّر افتراضيّا والتّي تُعتبر القيمة التّي تُختبر، بالنّسبة للمُعامل فقد سمّيناه number_of_characters لأنّه يُمثّل عدد المحارف الذي سنعتبره الحد الأقصى لسلسلة نصيّة، افتراضيّا قيمة المُعامل number_of_characters هي 30، ما يعني بأنّ الاختبار سيعمل بنفس طريقة عمل الاختبار long في المثال السّابق إذا لم تُوفّر قيمة للمُعامل. داخل الدّالة، الأمر مُشابه لطريقة عمل الاختبار long، لكن عوضا عن مُقارنة طول قيمة المُعامل value مع العدد 30، أصبحنا نُقارن الطّول مع قيمة المُعامل number_of_characters الذي نحصل عليه من مُستخدم الاختبار. هكذا سيكون لدينا اختبار يسمح لنا بالتّحقق ممّا إذا كانت سلسلة نصّيّة ما أطول من عدد مُعيّن أو لا، عوضا عن التّحقق ممّا إن كانت أطول من العدد 30 فقط. المثال التّالي توضيح لكيفيّة استخدام الاختبار longer_than الذي أنشأناه للتوّ: {% set title = 'An Introduction To Flask' %} {% if title is longer_than 40 %} 'Title is too long' {% else %} {{ title }} {% endif %} في هذا المثال، نُعرّف مُتغيّرا title ونمنحه سلسلة نصيّة قصيرة، بعدها نختبر هذا المُتغيّر باستعمال الجملة الشّرطيّة if والاختبار longer_than، نتأكّد في الاختبار من أنّ السّلسلة النّصيّة المتواجدة بالمُتغيّر title أطول من 40 محرفا، إن كانت كذلك فإنّنا نعرض الرّسالة 'Title is too long'، وإن لم تكن كذلك فإنّنا نعرضها على صفحة HTML بشكل طبيعي. بما أنّ العنوان غير طويل، فالنّتيجة ستكون عرض العنوان في الصّفحة وليس العكس: An Introduction To Flask لاختبار العكس استبدل سطر تعريف المُتغيّر title بما يلي: {% set title = 'Building A Web Application Using The Flask Framework' %} في هذه الحالة، العنوان أطول من 40 محرفا، لذا فالنّتيجة ستكون الجملة 'Title is too long'. مثال على اختبار للتّحقق ممّا إذا كانت سلسلة نصيّة ما تنتهي بمقطع مُعيّن في لغة بايثون، يُمكننا التّحقق من أنّ سلسلة نصيّة تنتهي بمقطع ما باستعمال التّابع endswith مع تمرير المقطع كمُعامل. مثال على كيفيّة استخدام التّابع endswith داخل مُفسّر لغة بايثون: >>> 'An Introduction To Flask'.endswith('Flask') True >>> 'An Introduction To Flask'.endswith('Python') False المُهمّة هي إنشاء اختبار Jinja للحصول على نفس النّتيجة، بحيث سيكون لدينا اختبار باسم endswith ويقبل مُعاملا واحدا، ألا وهو المقطع الذي سنتحقّق ممّا إذا كان مُتواجدا بنهاية السّلسلة المُختَبَرَةِ أو لا. ما يلي مثال على كيفيّة إنشاء الاختبار endswith: @app.template_test(name='endswith') def endswith_test(value, end): return value.endswith(end) الاختبار بسيط جدّا، إذ كل ما نقوم به هو استعمال التّابع endswith على القيمة value مع تمرير قيمة المُعامل end التّي تُعتبر المقطع الذي نرغب بالتّحقق من أنّ السّلسلة تنتهي به، وبما أنّ نتيجة استعمال التّابع ستكون إمّا القيمة True أو القيمة False، فالاختبار سيعمل دون مشاكل، لأنّ شرط اختبار أن تُرجع إحدى القيمتين. المثال التّالي توضيح لكيفيّة استعمال الاختبار endswith: {% set title = 'Building A Web Application Using Flask' %} {% if title is endswith('Flask') %} 'Title ends with the word Flask' {% else %} 'Title does not end with the word Flask' {% endif %} في المثال، نقوم بالتّعريف الاعتيادي للمُتغيّر title، مع تعيين عنوان ينتهي بالكلمة Flask، بعدها نستعمل الاختبار للتّحقق ممّا إذا كان المُتغيّر title ينتهي بالمقطع Flask أو لا، فإن كان كذلك فإنّنا نعرض الرّسالة 'Title ends with the word Flask' وإن لم يكن كذلك فالرّسالة تُصبح 'Title does not end with the word Flask'. وبما أنّ العنوان ينتهي بالمقطع Flask فالنّتيجة هي الحالة الأولى: 'Title ends with the word Flask' لاختبار الحالة الثّانية، غيّر سطر تعريف المُتغيّر title إلى جملة لا تنتهي بالمقطع Flask. خاتمة بنهاية هذا الدّرس، يجب أن تكون قادرا على كتابة واستخدام اختباراتك الخاصّة عند العمل مع كل من مُحرّك القوالب Jinja وإطار العمل Flask، في الدّروس القادمة، سنُكمل التّعرف على أساسيات مُحرّك القوالب لتتمكّن من تطوير تطبيقات Flask أفضل.
  13. تمهيد بايثون هي أداةٌ رائعةٌ لمعالجة البيانات. ومن المحتمل أنَّ البرامج التي تكتبها ستتضمن قراءة البيانات أو كتابتها أو تعديلها، ولهذا السبب ستستفيد جدًا من قدرة بايثون على التعامل مع مختلف صيغ الملفات التي تُخزِّن أصنافًا متنوعةً من البيانات. ليكن لديك -على سبيل المثال- برنامج بايثون الذي يتحقق من امتيازات الوصول لقائمة من المستخدمين؛ فيكون من المرجح في هذه الحالة أن تُخزَّن قائمة المستخدمين في ملفٍ نصيٍ بسيط. أو ربما لم تكن تتعامل مع النصوص، وإنما مهمة برنامجك هي التحليل الاقتصادي؛ ومن المرجح أنَّك ستأخذ الأرقام التي ستجري عملياتك عليها من ورقة عمل في أحد البرامج مثل Excel أو Calc. وأرى أنَّه من المرجح أن يتطلب تطبيقك إدخال أو إخراج البيانات بغض النظر عن الهدف منه. سيشرح هذا الدرس باختصار بعض الصيغ التي تستطيع بايثون التعامل معها. سنستعرض أولًا لمحة عن بعض أنواع الملفات، ثم سنكمل الدرس بكيفية فتح وقراءة الملفات والكتابة إليها في بايثون 3. يجب أن تُصبح قادرًا بعد نهاية هذا الدرس على التعامل مع أيّة ملفات نصية في بايثون. المتطلبات المسبقة ستحتاج في هذا الدرس إلى توافر بايثون 3 على حاسوبك بالإضافة إلى بيئة برمجية محلية مضبوطة عليه. لغة بايثون هي لغة متكاملة وتستطيع التعامل بسهولة (نسبيًا) مع مختلف صيغ الملفات، بما فيها الصيغ الآتية: txt: ملف نصي بسيط يُخزِّن البيانات التي تُمثِّل المحارف (أو السلاسل النصية) ولا تضم أيّة بيانات وصفية. CSV: ملف يحتوي قيمًا مفصولٌ بينها بفاصلة (أو غيرها من المحارف التي تستعمل لفصل الحقول) لتنظيم بُنية هيكلية للبيانات، مما يسمح بحفظ المعلومات بصيغةٍ مجدولة. HTML: لغة توصيفية تُخزِّن البيانات الهيكلية وتستعمل عادةً لبناء صفحات الويب. JSON: صيغة بسيطة وعملية، مما يجعلها أشهر الصيغ المستعملة لتخزين ونقل البيانات. سنُركِّز في هذا الدرس على صيغة txt. الخطوة الأولى: إنشاء ملف نصي قبل أن نبدأ بإنشاء برامج بايثون، سنحتاج إلى ملفٍ نصيٍ لنعمل عليه؛ لذا افتح محررك النصي المُفضَّل وأنشِئ ملف txt جديد واجعل اسمه days.txt. أدخِل في الملف الجديد بضعة أسطر، ولتكن مثلًا أيام الأسبوع: Monday Tuesday Wednesday Thursday Friday Saturday Sunday احفظ الآن الملف وانتبه إلى مكان تخزينه. فمثلًا، كان اسم المستخدم في نظامي هو sammy وحفظتُ الملف في المسار ‎/users/sammy/days.txt. هذا المسار مهمٌ جدًا في الخطوات القادمة، لأننا سنحاول فتحه باستخدام بايثون. بعد أن حصلنا على ملفٍ نصيٍ لنعمل عليه، فلنبدأ عملية كتابة البرنامج. الخطوة الثانية: فتح الملف قبل أن نبدأ بكتابة البرنامج علينا إنشاء ملف لحفظه، لذا سنُنشِئ الملف files.py في المحرر النصي. وللتبسيط سنضعه في نفس المجلد الذي يحتوي الملف days.txt: أي ‎/users/sammy/‎. علينا لنفتح ملف في بايثون أن نعثر على طريقةٍ لربط الملف الموجود على القرص الصلب بأحد المتغيرات. تدعى هذه العملية «بفتح» الملف. سنبدأ بإخبار بايثون أين يوجد الملف، ويُشار عادةً إلى مكان تخزين الملف بالمصطلح «المسار» (path). ولكي تستطيع لغة بايثون أن تفتح لك الملف، فستحتاج إلى مساره. ومسار الملف days.txt كما ذكرنا في القسم السابق هو ‎/users/sammy/days.txt، وسنُنشِئ متغيرًا لتخزين هذا المسار باسم path في ملف files.py وضبط قيمته إلى مسار الملف days.txt: path = '/users/sammy/days.txt' سنستخدم بعد ذلك الدالة open()‎ الموجودة في بايثون لفتح الملف days.txt. تتطلب الدالة open()‎ تمرير مسار الملف كأوّل وسيط (argument) لها، لكنها تقبل عددًا كبيرًا من المعاملات (parameters)؛ لكن أهم معامل منها هو المعامل الاختياري الذي يُحدِّد «نمط» فتح الملف (opening mode)، فالنمط هو سلسلةٌ نصيةٌ تُحدِّد ماذا تستطيع أن تفعله مع الملف. هذه قائمة ببعض الأنماط المتوافرة: 'r': فتح الملف للقراءة 'w': فتح الملف للكتابة 'x': إنشاء الملف وفتحه للكتابة 'a': الإضافة إلى نهاية الملف 'r+‎': فتح الملف للقراءة والكتابة معًا أريد أن أقرأ من الملف فقط في المثال الآتي، لذا سأستخدم النمط r. سنستعمل الدالة open()‎ لفتح الملف days.txt وإسناده إلى المتغير days_file. days_file = open(path,'r') نستطيع الآن بعد فتح الملف أن نقرأ منه، وهذا ما سنناقشه في القسم التالي. الخطوة الثالثة: قراءة الملف نستطيع الآن بعد فتح الملف أن نُجري عمليات عليه (مثلًا: أن نقرأ منه) عبر المتغير الذي أسندناه إليه. توفِّر بايثون ثلاثة عمليات متعلقة بقراءة المعلومات من ملف، وسأريك إياها كلها في أمثلةٍ لتفهم كيف تعمل. أوّل عملية هي ‎<file>.read()‎ التي تُعيد كامل محتويات الملف كسلسلةٍ نصيةٍ وحيدة. days_file.read() الناتج: 'Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday\n' العملية الثانية ‎<file>.readline()‎ تُعيد السطر التالي من الملف، حيث تُعيد السطر بأكمله بالإضافة إلى محرف السطر الجديد. ببساطة: هذه العملية ستؤدي إلى قراءة الملف سطرًا بسطر. days_file.readline() الناتج: 'Monday\n' وبعد أن تقرأ سطرًا عبر readline()‎ فسيتم الانتقال إلى السطر الذي يليه، فلو استدعيتَ هذه العملية مرةً أخرى، فستُعيد السطر الذي يلي السطر السابق في الملف كما في الناتج الآتي: 'Tuesday\n' آخر عملية هي ‎<file>.readlines()‎ التي تُعيد قائمةً (list) بجميع الأسطر الموجودة في الملف، حيث يُمثَّل كل سطر في الملف بعنصرٍ في القائمة. days_file.readlines() الناتج: ['Monday\n', 'Tuesday\n', 'Wednesday\n', 'Thursday\n', 'Friday\n', 'Saturday\n', 'Sunday\n'] أحد الأمور التي يجب عليك أن تبقيها في ذهنك عند القراءة من الملفات هو عدم قدرتك على إعادة قراءة الملف بعد استخدام إحدى عمليات القراءة السابقة. فمثلًا لو استدعيتَ days_file.read()‎ متبوعةً بالدالة days_file.readlines()‎ فستُعيد العملية الثانية سلسلةً نصيةً فارغةً، وبالتالي ستحتاج إلى إنشاء متغير جديد مرتبط بالملف في كل مرة ترغب فيها بقراءته. بعد أن تعلمنا طرائق القراءة من ملف فحان الوقت الآن إلى تعلم كيفية الكتابة إلى ملفٍ جديد. الخطوة الرابعة: الكتابة إلى ملف سنكتب الآن السلسلة النصية «Days of the Week» إلى ملفٍ جديد متبوعةً بأيام الأسبوع. سنُنشِئ بادئ الأمر المتغير title: title = 'Days of the Week\n' سنحتاج أيضًا إلى تخزين أيام الأسبوع في متغيرٍ الذي سنسميه days. ولتبسيط ذلك سنضع نفس الشيفرة التي استخدمناها في القسم أعلاه؛ حيث سنفتح الملف بنمط القراءة ثم نقرأه ونُخزِّن ناتج عملية القراءة في المتغير الجديد days: path = '/users/sammy/days.txt' days_file = open(path,'r') days = days_file.read() أصبح لدينا الآن متغيران أولهما للعنوان والآخر لأيام الأسبوع، لذا نستطيع البدء بالكتابة إلى الملف الجديد. لكننا سنحتاج أولًا إلى تحديد مسار الملف، وسنستخدم المجلد ‎/users/sammy مجددًا، ثم سنُحدِّد اسم الملف الجديد الذي نود إنشاءه؛ لذا سيكون المسار النهائي هو ‎/users/sammy/new_days.txt، وسنضع هذا المسار في المتغير new_path، ثم سنفتح الملف الجديد بنمط الكتابة، وذلك باستخدام الدالة open()‎ مع النمط w: new_path = '/users/sammy/new_days.txt' new_days = open(new_path,'w') من المهم أن تعلم أنَّه لو كان الملف new_days.txt موجودًا من قبل «فتح» الملف، فستُحذف جميع محتوياته السابق، لذا توخى الحذر عند استخدام النمط w. بعد أن فتحنا الملف للقراءة، سنستطيع الآن أن نكتب البيانات فيه وذلك باستخدام الدالة ‎<file>.write()‎. تأخذ دالة الكتابة معاملًا وحيدًا الذي يجب أن يكون سلسلةً نصية وتكتبه إلى الملف. إذا أردتَ أن تبدأ سطرًا جديدًا في الملف، فعليك توفير محرف بداية السطر (newline) يدويًا. سنكتب أولًا العنوان إلى الملف متبوعًا بأيام الأسبوع. ولنضف أيضًا بعض تعبيرات print لنعرف ما الذي سيُكتَب إلى الملف، وهذا أمرٌ مستحسنٌ لكي تتتبع مسار تنفيذ برنامجك. new_days.write(title) print(title) new_days.write(days) print(days) بعد أن تنتهي من التعامل مع ملفٍ ما، فاحرص على أن تغلقه، وهذا ما سنفعله في الخطوة الأخيرة. الخطوة الخامسة: إغلاق الملف عملية إغلاق الملف تعني إغلاق قناة التواصل بين الملف على القرص وبين المتغير الموجود في برنامجك. إغلاق الملفات يعني أنَّ البرامج الأخرى ستتمكن من الوصول إلى الملف وستُبقي بياناتك بأمان؛ لذا احرص دومًا على إغلاق الملفات التي تفتحها؛ وهذا ما سنفعله في الأسطر الآتية عبر استعمال الدالة ‎<file>.close()‎: days_file.close() new_days.close() أنهينا عملية معالجة الملفات في بايثون، ولننظر الآن إلى الشيفرة النهائية. الخطوة السادسة: التحقق من سلامة الشيفرة قبل أن تجرِّب الشيفرة تأكَّد أنَّ كل شيء يبدو سليمًا. يجب أن تكون الشيفرة النهائية شبيهةً بما يلي: path = '/users/sammy/days.txt' days_file = open(path,'r') days = days_file.read() new_path = '/users/sammy/new_days.txt' new_days = open(new_path,'w') title = 'Days of the Week\n' new_days.write(title) print(title) new_days.write(days) print(days) days_file.close() new_days.close() بعد أن تحفظ الشيفرة في ملفها، فافتح الطرفية (terminal) وشغِّل سكربت بايثون: python files.py يجب أن يبدو الناتج كما يلي: Days of the Week Monday Tuesday Wednesday Thursday Friday Saturday Sunday لنتحقق الآن من أنَّ الشيفرة تعمل كما ينبغي لها وذلك بفتح الملف الجديد المُنشَأ new_days.txt فلو نُفِّذ البرنامج تنفيذًا صحيحًا فيجب أن تكون مخرجاته كالآتي: Days of the Week Monday Tuesday Wednesday Thursday Friday Saturday Sunday إذا ظهر معك نفس الناتج السابق فأود أن أهنِّئك على إتمامك لهذا الدرس بنجاح. الخلاصة تعلمنا في هذا الدرس كيفية التعامل مع الملفات النصية البسيطة في بايثون 3 ومعالجتها. يمكنك الآن أن تفتح الملفات وتقرأها وتكتب إليها ثم تغلقها. جرِّب ما تعلمته في هذا الدرس على ملفات التي تريد معالجتها واسأل إن واجهتَ صعوبةً في ذلك. ترجمة -وبتصرّف- للمقال How To Handle Plain Text Files in Python 3 لصاحبته Michelle Morales
  14. تمهيد Python هي لغةٌ سهلة القراءة للغاية ومتنوعة ومتعددة الاستخدامات، واسمها مستوحى من مجموعة كوميدية بريطانية باسم «Monty Python»، وكان أحد الأهداف الأساسية لفريق تطوير بايثون هو جعل اللغة مرحةً وسهلة الاستخدام، وإعدادها بسيطٌ، وطريقة كتابتها مباشرة وتعطيك تقريرًا مباشرًا عند حدوث أخطاء، وهي خيارٌ ممتازٌ للمبتدئين والوافدين الجدد على البرمجة. لغة بايثون هي لغة متعددة الاستعمالات، وتدعم مختلف أنماط البرمجة مثل كتابة السكربتات والبرمجة كائنية التوجه (object-oriented)، وهي مناسبةٌ للأغراض العامة، واستعمالها يتزايد في سوق العمل إذ تعتمدها منظماتٌ مثل «United Space Alliance» (شركة في مجال إرسال مركبات فضائية وتتعاقد معها ناسا) و «Industrial Light & Magic» (أستوديو للتأثيرات السينمائية وللرسوم المتحركة)، وتوفِّر بايثون قدراتٍ كثيرةٍ لمن يريد تعلم لغة برمجة جديدة. طوِّرَت اللغة في نهاية الثمانينات من القرن الماضي، ونُشِرَت أوّل مرة في عام 1991، طُوِّرَت بايثون من قِبل Guido van Rossum، وهو عضوٌ نشطٌ للغاية في المجتمع. وتعتبر بايثون على أنَّها بديلٌ عن لغة ABC، وأوّل إصدار منها كان يتضمن التعامل مع الاستثناءات (exception handling) والدوال والأصناف (classes) مع إمكانية الوراثة فيها. وعدما أُنشِئ منتدى محادثة في Usenet باسم comp.lang.python في 1994، فبدأت قاعدة مستخدمي بايثون بالنمو، مما مهّد الطريق لها لتصبح واحدة من أكثر لغات البرمجة شيوعًا وخصوصًا لتطوير البرمجيات مفتوحة المصدر. لمحة عامة قبل أن ننظر إلى إمكانيات إصدارَي بايثون 2 وبايثون 3 (مع الاختلافات البرمجية الرئيسية بينهما)، فلننظر إلى لمحة تاريخية عن الإصدارات الرئيسية الحديثة من بايثون. بايثون 2 نُشِرَ هذا الإصدار في أواخر عام 2000، وأصبحت بايثون 2 لغة برمجة شاملة مقارنةً بالإصدارات التي تسبقها وذلك بعد تطبيق اقتراح PEP ‏(Python Enhancement Proposal)، وهو مواصفةٌ (specification) تقنيةٌ التي توفِّر معلومات إلى أعضاء مجتمع بايثون أو تصف ميزاتٍ جديدة في اللغة. بالإضافة إلى ذلك، تضمنت بايثون 2 ميزاتٍ برمجية جديدة مثل «cycle-detecting garbage collector» لأتمتة عملية إدارة الذاكرة، وزيادة دعم يونيكود لتدعم اللغة جميع المحارف المعيارية …إلخ. وأثناء عملية تطوير بايثون 2 أضيفت ميزات جديدة بما في ذلك توحيد الأنواع والأصناف في بايثون في بنية هيكلية وحيدة (وذلك في إصدار 2.2 من بايثون). بايثون 3 تُعتَبر بايثون 3 هي مستقبل لغة بايثون وهي النسخة قيد التطوير من اللغة، وهذا إصدارٌ رئيسيٌ نُشِر في أواخر عام 2008 لإصلاح بعض المشاكل الجوهرية في تصميم الإصدارات السابقة من اللغة، وكان التركيز أثناء تطوير بايثون 3 هو تحسين الشيفرات التي تبنى عليها اللغة وحذف التكرارات، مما يعني أنَّ هنالك طريقة وحيدة فقط لإنجاز مهمّة معيّنة. التعديلات الأساسية التي حدثت في بايثون 3.0 تتضمن تغير العبارة print إلى دالة مُضمَّنة باللغة، وتحسين قسمة الأعداد الصحيحة، وتوفير دعم إضافي ليونيكود. في البداية، انتشرت بايثون 3 ببطء نتيجةً لعدم توافقيتها مع بايثون 2، مما يعني أنَّ على المستخدمين اختيار ما هو الإصدار الذي عليهم استخدامه. بالإضافة إلى ذلك، كانت الكثير من المكتبات البرمجية متاحةً فقط لبايثون 2، لكن بعد تقرير فريق تطوير بايثون 3 أنَّه يجب أن التخلي عن دعم بايثون 2، فبدأت عملية تحويل المكتبات إلى بايثون 3. يمكننا معرفة زيادة الاعتماد على بايثون 3 من خلال عدد الحزم البرمجية التي تدعم بايثون 3، والتي هي (في وقت كتابة هذا المقال) 339 من أصل 360 من أشهر الحزم. بايثون 2.7 بعد إصدار بايثون 3.0 في 2008، أُصدِرَت نسخة بايثون 2.7 في تموز 2010 وهي آخر إصدار من سلسلة ‎2.x، الغرض من إصدار بايثون 2.7 هو جعل الطريق ممهدًا أمام مستخدمي بايثون ‎2.x لتحويل برامجهم إلى بايثون 3 بتوفير بعض التوافقية بينهما. وهذه التوافقية تضمنت دعم بعض الوحدات المُحسّنة في 2.7 مثل unittest لأتمتة الاختبارات، و argparse لتفسير خيارات سطر الأوامر، وبعض الفئات في collections. ولخصوصية بايثون 2.7 ولكونها جسرًا واصلًا بين الإصدارات القديمة من بايثون 2 وبين بايثون 3.0، فأصبحت خيارًا شائعًا بين المبرمجين بسبب توافقيتها مع الكثير من المكتبات. عندما نتحدث اليوم عن بايثون 2، فنحن نشير عادةً إلى إصدار بايثون 2.7 لأنَّه أكثر إصدار مستخدم؛ لكنه يُعتَبَر أنَّه إصدارٌ قديم، وسيتوقف تطويره (التطوير الحالي هو إصلاح العلل فقط) تمامًا في 2020. الاختلافات الأساسية بغض النظر أنَّ بايثون 2.7 وبايثون 3 تتشاركان في الكثير من الأشياء، لكن لا يجدر بك أن تظن أنَّهما متماثلتان ويمكن تبديل الشيفرات بينهما. وعلى الرغم من أنَّك تستطيع كتابة شيفرات جيدة وبرامج مفيدة في أيّ إصدار منهما، لكن من المهم أن تفهم أنَّ هنالك بعض الاختلافات في بنية الشيفرات وفي طريقة تفسيرها. سأعرض هنا بعض الأمثلة، لكن عليك أن تعلم أنَّك ستواجه المزيد من الاختلافات أثناء مسيرة تعلمك لبايثون. print في بايثون 2، تُعامَل print كتعبيرٍ برمجيٍ بدلًا من كونها دالة، وهذا كان يثير ارتباكًا لأنَّ الكثير من الأمور داخل بايثون تتطلب تمرير وسائط (arguments) بين قوسين، إذا فتحتَ مُفسِّر بايثون 2 لطباعة «Sammy the Shark is my favorite sea creature»، فستكتب تعبير print الآتي: print "Sammy the Shark is my favorite sea creature" أما في بايثون 3، فستُعامَل print()‎ كدالة، لذا لطباعة السلسلة النصية السابقة، فيمكننا استخدام شكل استدعاء الدوال التقليدي كما يلي: print("Sammy the Shark is my favorite sea creature") هذا التعديل جعل من البنية اللغوية في بايثون موحدةً وسهَّلَ من التبديل بين مختلف دوال الطباعة فيها. يجدر بالذكر أنَّ الدالة print()‎ متوافقة مع بايثون 2.7، لذا ستعمل شيفرات بايثون التي تستعمل print()‎ بشكلٍ صحيحٍ في أيّ الإصدارَين. قسمة الأعداد الصحيحة في بايثون 2، أيُّ عددٍ تكتبه دون فواصل عشرية سيُعامَل على أنَّه من النوع integer، تأتي الإشكالية عندما تحاول قسمة الأعداد الصحيحة على بعضها، فتتوقع في بعض الأحيان حصولك على عددٍ عشري (تسمى أيضًا بالأعداد ذات الفاصلة العائمة float) كما في التعبير الرياضي: 5 / 2 = 2.5 لكن الأعداد الصحيحة في بايثون 2 لن تتحول إلى أعداد عشرية عندما تتطلب العملية التي تُجرى عليها ذلك. عندما يكون العددان الموجودان على جانبَي معامل القسمة / عددين صحيحين، فإن بايثون 2 ستجري عملية القسم وستُنتِج عددًا عشريًا إلا أنها ستُعيد العدد الصحيح الأصغر أو المساوي للناتج، وهذا يعني أنَّه لو كتبتَ ‎5 / 2 فستُعيد بايثون 2.7 العدد الصحيح الأصغر أو المساوي للعدد 2.5، وهو في هذه الحالة 2: a = 5 / 2 print a 2 لإعادة عدد عشري، فيجب إضافة فواصل عشرية إلى الأرقام التي ستُجري عليها عملية القسمة كما في ‎5.0 / 2.0 لكي تحصل على النتيجة المنطقية 2.5. أما في بايثون 3، فقسمة الأعداد الصحيحة أصبحت كما نتوقع: a = 5 / 2 print a 2.5 يمكنك استخدام ‎5.0 / 2.0 لإعادة 2.5، لكن إن أردتَ تقريب ناتج القسمة فاستخدم المعامل // الموجود في بايثون 3، كالآتي: a = 5 // 2 print a 2 هذا التعديل في بايثون 3 جعل من قسمة الأعداد الصحيحة أمرًا سهلًا، لكن هذه الميزة غير متوافقة مع بايثون 2.7. دعم محارف يونيكود عندما تتعامل لغات البرمجة مع السلاسل النصية (strings، والتي هي سلسلةٌ من المحارف)، فهي تفعل ذلك بطرائق مختلفة لكي تتمكن الحواسيب من تحويل الأعداد إلى أحرف ورموز. تستعمل بايثون 2 محارف ASCII افتراضيًا، لذا عندما تكتب "Hello, Sammy!‎" فستتعامل بايثون 2 مع السلسلة النصية كمجموعة من محارف ASCII، والتي هي محدودةٌ لحوالي مئتَي محرف، أي أنَّ محارف ASCII هي طريقة غير عملية لترميز المحارف خصوصًا المحارف غير اللاتينية (كالعربية مثلًا). لاستخدام ترميز محارف يونيكود (Unicode) الذي يدعم أكثر من 128000 محرف تابع للكثير من اللغات والرموز، فعليك أن تكتب u"Hello, Sammy!‎"‎ حيث تُشير السابقة u إلى Unicode. تستعمل بايثون 3 محارف يونيكود (Unicode) افتراضيًا، مما يوفِّر عليك بعض الوقت أثناء التطوير، ويمكنك كتابة وعرض عدد أكبر بكثير من المحارف في برنامجك بسهولة. يدعم يونيكود الكثير من المحارف بما في ذلك الوجوه التعبيرية (emojis)، واستعمالها كترميز محارف افتراضي يعني أنَّ الأجهزة المحمولة ستكون مدعومةً في مشاريعك تلقائيًا. إذا كنت تحب أنَّ تكون شيفرات بايثون 3 التي تكتبها متوافقةً مع بايثون 2، فأبقِ على حرف u قبل السلاسل النصية. استمرار التطوير الفارق الرئيسي بين بايثون 3 وبايثون 2 ليس في البنية اللغوية وإنما في أنَّ إصدار بايثون 2.7 سيتوقف دعمه في 2020، وسيستمر تطوير بايثون 3 بميزاتٍ جديدة وإصلاحٍ لمزيدٍ من العلل. التطويرات الأخيرة في اللغة تتضمن تخصيصًا أبسط لإنشاء الأصناف، وطريقةً أوضح للتعامل مع المصفوفات… الاستمرار بتطوير بايثون 3 يعني أنَّ المطورين يمكن أن يعتمدوا على اللغة، وسيطمئنون أنَّ المشاكل التي قد تحدث فيها ستُحَل في فترةٍ قريبة، ويمكن أن تصبح البرامج أكثر كفاءة بإضافة المزيد من الميزات للغة. نقاطٌ أخرى يجب أخذها بعين الاعتبار عليك أن تضع النقاط الآتية بعين الاعتبار عندما تبدأ مشوارك كمبرمج بلغة بايثون، أو عندما تبدأ بتعلم لغة بايثون بعد تعلمك لغيرها. إذا كنتَ تأمل بتعلم اللغة دون أن تفكِّر بمشروعٍ معيّن، فأنصحك بالتفكير بمستقبل بايثون، فسيستمر تطوير ودعم بايثون 3 بينما سيوقف دعم بايثون 2.7 عمّا قريب. أما إذا كنتَ تُخطِّط للانضمام لفريق تطوير أحد المشاريع، فعليك أن تنظر ما هو إصدار بايثون المستخدم فيه، وكيف يؤدي اختلاف الإصدار إلى اختلاف طريقة تعاملك مع الشيفرات، وإذا ما كانت المكتبات البرمجية المستعملة في المشروع مدعومةً في مختلف الإصدارات، وما هي تفاصيل المشروع نفسه… إذا كنت تُفكّر ببدء أحد المشاريع، فيجدر بك أن تنظر ما هي المكتبات المتوفرة وما هي إصدارات بايثون المدعومة. وكما قلنا سابقًا، الإصدارات الأوليّة من بايثون 3 لها توافقية أقل مع المكتبات المبنية لبايثون 2، لكن الكثير منها قد جرى تحويله إلى بايثون 3، وسيستمر ذلك في السنوات الأربع المقبلة. الخلاصة لغة بايثون كبيرة جدًا وموثقة توثيقًا ممتازًا وسهلة التعلم، ومهما كان اختيارك (بايثون 2 أو بايثون 3) فستتمكن من العمل على المشاريع الموجودة حاليًا. صحيحٌ أنّ هنالك بعض الاختلافات المحورية، لكن ليس من الصعب الانتقال من بايثون 3 إلى بايثون 2، وستجد عادةً أنَّ بايثون 2.7 قادرة على تشغيل شيفرات بايثون 3، خصوصًا في بدايات تعلمك للغة. من المهم أن تبقي ببالك أنَّ تركيز المطورين والمجتمع أصبح منصبًّا على بايثون 3، وسيصبح هذه اللغة رائدةً في المستقبل وستلبي الاحتياجات البرمجية المطلوبة، وأنَّ دعم بايثون 2.7 سيقل مع مرور الزمن إلى أن يزول في 2020. ترجمة -وبتصرّف- للمقال [Python 2 vs Python 3: Practical Considerations] لصاحبته Lisa Tagliaferri
  15. مُقدّمة بعد التّعرف في الدّرس السّابق على مفهوم الاختبارات (Tests) في مُحرّك القوالب Jinja، سنتعرّف في هذا الدّرس على بعض من أهمّ الاختبارات التّي يجب عليك الإلمام بها لتطوير تطبيقات ويب أفضل بشكل أسهل وأسرع مع إطار العمل Flask، بعد التّعرف على هذه الاختبارات، يُمكنك استعمالها ببساطة للتّحقق من أنّ القيمة المتواجدة بمُتغيّر ما هي ما تُريده بالفعل، كما ستتمكّن من تنفيذ إجراءات حسب حالة المُتغيّر. الاختبار iterable قُلنا من قبل بأنّ الكائنات من النّوع iterable عبارة عن أي كائن يُمكنك الدّوران حوله باستخدام حلقة for، هذا يشمل مجموعات العناصر مثل القوائم والسّلاسل النّصيّة وأنواع أخرى من القيم، إذا لم تكن متأكّدا من أنّ كائنا ما عبارة عن كائن من النّوع Iterable فيُمكنك استخدام الاختبار iterable للتّحقق من الأمر بشكل بسيط. مثلا، يُمكن أن تجد بأنّك ستحتاج إلى الدّوران حول مُتغيّر لكنّك لست متأكّدا من أنّه سيكون دائما قابلا لذلك، لذا لتفادي عرض الأخطاء للمُستخدم، يُمكنك استخدام الاختبار iterable مع جملة if شرطيّة، وهكذا ستتمكّن من الدّوران حول المُتغيّر فقط إذا كان من النّوع Iterable أمّا إن لم يكن كذلك فسيتم تجاهله إلى أن تتغيّر قيمته إلى نوع قابل للدّوران عليه. المثال التّالي توضيح لكيفيّة استعمال الاختبار Iterable: {% set variable = 'ABCD' %} {% if variable is iterable %} <ul> {% for item in variable %} <li>{{ item }}</li> {% endfor %} </ul> {% else %} لا يُمكنك الدّوران حول قيمة المُتغيّر {% endif %} المثال بسيط وواضح، إذ نقوم بتعريف المُتغيّر variable ونتحقّق من أنّه من النّوع Iterable عن طريق كل من الجملة الشّرطيّة if والاختبار iterable، إذا كانت قيمة المُتغيّر قابلة للدّوران عليها، فإنّنا نقوم باستعمال الحلقة for للوصول إلى كل عنصر وعرضه على شكل عنصر من قائمة HTML، أمّا إن لم تجتز قيمة المُتغيّر الاختبار فإنّنا نعرض الجملة “لا يُمكنك الدّوران حول قيمة المُتغيّر”. بما أنّنا نستطيع الدّوران حول أي سلسلة نصيّة في لغة بايثون للوصول إلى عناصرها، فنتيجة المثال ستكون قائمة HTML لعرض عناصر السلسلة النّصيّة ABCD. لنتأكّد من أنّ المثال يعمل في كلتا الحالتين، غيّر السّطر الأول إلى ما يلي: {% set variable = 100 %} في هذه الحالة، قيمة المُتغيّر هي العدد الصّحيح 100، والأعداد الصّحيحة نوع لا يُمكن أن تدور حوله (إلّا إذا ما حوّلته إلى سلسلة نصيّة)، لذا فالنّتيجة هذه المرّة ستكون الجملة “لا يُمكنك الدّوران حول قيمة المُتغيّر ”. الاختبار lower عند التّعامل مع الأحرف اللاتينيّة، قد ترغب بالتّحقق من أنّ قيمة مُتغيّر عبارة عن أحرف صغيرة (Lowercase)، وهذا بالضّبط ما يُمكنك فعله بالاختبار lower. المثال التّالي يُوضّح طريقة عمل الاختبار lower: {% set words = ['ORM', 'walk', 'run', 'OOP'] %} {% for word in words %} {% if word is lower %} {{ word }} {% else %} {{ word }} is an acronym. {% endif %} <br> {% endfor %} في المثال أعلاه، نُعرّف قائمة باسم words تحتوي على بضعة كلمات، بعضها عبارة عن كلمات عاديّة والبعض الآخر عبارة عن اختصارات، وكما تعلم فهذه الأخيرة تُكتب بأحرف كبيرة لذا سنتمكّن من فصلها عن الكلمات الأخرى باستخدام الاختبار lower لأنّ الكلمات العاديّة مكتوبة بأحرف صغيرة، ما يعني بأنّها ستجتاز الاختبار، أمّا الاختصارات فلن تجتازه، لذا نقوم بالدّوران حول القائمة واختبار كل عنصر، فإن كانت الكلمة تحتوي على الأحرف الصّغيرة فقط فإنّنا نعرضها أمّا إن لم تكن كذلك فإنّنا نعرض رسالة تُفيد بأنّ العنصر عبارة عن اختصار لعبارة ما. نتيجة المثال أعلاه ستكون كما يلي: ORM is an acronym. walk run OOP is an acronym. الاختبار upper هذا الاختبار هو عكس الاختبار lower، إذ يُستعمل للتّحقق من أنّ كلمة ما مكتوبة بأحرف كبيرة، وطريقة استخدامه مُشابهة لطريقة استخدام الاختبار lower. مثال على كيفيّة استخدام الاختبار upper: {% set words = ['ORM', 'walk', 'run', 'OOP'] %} {% for word in words %} {% if word is upper %} {{ word }} <br> {% endif %} {% endfor %} النّتيجة: ORM OOP الاختبار undefined تعرّفنا من قبل على الاختبار defined، وقلنا بأنّه يُستعمل للتّحقق من أنّ مُتغيّرا ما معرّف أو لا، الاختبار undefined يقوم بالعكس، إذ يرجع القيمة المنطقيّة True إذا لم يكن المُتغيّر مُعرّفا، والقيمة False إذا كان المُتغيّر مُعرّفا. الاختبار none في لغة بايثون، إذا كان مُتغيّر ما يحمل القيمة None فهذا يعني بأنّ المُتغيّر مُعرّف لكنّه فارغ ولا يحمل أية قيمة، هذه القيمة مُهمّة جدّا إذا كنت تتعامل مع بعض المكتبات التّي تُساعد على التّعامل مع قواعد البيانات مثل مكتبة SQLAlchemy التّي سنعتمد عليها بشكل أساسيّ في مشروع “كلمة” الذي نبنيه في هذه الدّروس، لذا فمعرفة الغرض من القيمة None مهم. الاختبار none الذي يُمكنك استعماله في قوالب Jinja سيُساعدك على التّحقق ممّا إذا كانت قيمة الكائن الذي تتعامل معه فارغة أو لا، وبالتّالي ستتمكّن من عرض رسالة مفهومة للمُستخدم في حالة كانت نتيجة ما يبحث عنه هي القيمة None، فمثلا يُمكنك عرض الرّسالة “هذا المُستخدم لم يُضف نبذة عن نفسه بعد” في الصفحة الشّخصيّة للمُستخدمين الذين لم يقوموا بإضافة نبذة شخصيّة إلى حساباتهم، وهكذا سيعرف الزائر سبب غياب أي وصف في المكان المخصّص لذلك. المثال التّالي يُوضّح استعمال الاختبار none مع جملة شرطيّة لعرض رسالة تنبيه في حالة كانت قيمة العنصر فارغة: {% set list = [None, 'One', None, 'Two'] %} {% for item in list %} {% if item is none %} قيمة العنصر فارغة <br> {% else %} {{ item }} <br> {% endif %} {% endfor %} في المثال أعلاه، مزجنا بين السّلاسل النّصيّة والقيمة None في عناصر القائمة list، بعدها نقوم بالدّوران حول القائمة بحلقة for، ثمّ بعد ذلك نتحقّق ممّا إذا العنصر الحالي عبارة عن قيمة فارغة، فإن كان الأمر كذلك فإنّنا نعرض الرّسالة “قيمة العنصر فارغة”، أمّا إن لم يكن العنصر فارغا فإنّنا نعرضه. النّتيجة: قيمة العنصر فارغة One قيمة العنصر فارغة Two الاختبار number لمعرفة ما إذا كانت قيمة كائن ما عبارة عن عدد أو لا، يُمكنك استخدام الاختبار number، مع التّأكيد على أنّ الأعداد العشريّة تُعتبر أعدادا كذلك، لذا ستجتاز الاختبار بشكل عادي. المثال التّالي توضيح على كيفيّة استخدام الاختبار number: {% set list = [1, 'Flask', 2, 3, 3.2, 0.001] %} {% for item in list %} {% if item is number %} {{ item }} عبارة عن عدد <br> {% else %} {{ item }} ليس بعدد <br> {% endif %} {% endfor %} في هذا المثال، نقوم بتعريف قائمة تحتوي على أعداد وسلاسل نصيّة، ندور حول القائمة باستعمال حلقة for ثمّ نستعمل الاختبار number للتّحقّق ممّا إذا كان العنصر عددا أو لا، فإن كان عددا فإنّنا نعرض رسالة تُفيد بأنّ العنصر عبارة عن عدد بالفعل، أمّا إن لم يكن العنصر كذلك فإنّنا نعرض رسالة مفادها بأنّ القيمة ليست بعدد. نتيجة المثال: 1 عبارة عن عدد Flask ليس بعدد 2 عبارة عن عدد 3 عبارة عن عدد 3.2 عبارة عن عدد 0.001 عبارة عن عدد لاحظ في النّتيجة بأنّ الأعداد العشريّة تجتاز الاختبار كذلك. الاختبار string للتّحقق من أنّ قيمة ما عبارة عن سلسلة نصيّة، يُمكنك استخدام الاختبار string الذي يعمل بطريقة مُشابهة لطريقة عمل الاختبار number، ويُمكنك استعماله عند التّعامل مع البيانات التّي يُمكن أن تكون سلاسل نصيّة كما يُمكن أن تكون قيما أخرى في قاعدة البيانات. مثال على كيفيّة استخدام الاختبار string: {% set list = ['Flask', 2017, None] %} {% for item in list %} {% if item is number %} {{ item }} عبارة عن عدد <br> {% elif item is string %} {{ item }} عبارة عن سلسلة نصيّة <br> {% else %} {{ item }} ليس بعدد وليس سلسلة نصيّة <br> {% endif %} {% endfor %} في المثال أعلاه، نقوم بإنشاء قائمة ثمّ ندور عليها كالعادة، نختبر ما إذا كان كل عنصر عبارة عن عدد بالاختبار number لعرض رسالة تُفيد بذلك، إذا لم تكن القيمة عددا فإنّنا نستعمل الاختبار string لنرى إذا ما كانت القيمة سلسلة نصيّة لنعرض رسالة مفادها أنّ القيمة سلسلة نصيّة بالفعل، إذا لم تكن القيمة لا عددا ولا سلسلة نصيّة فإنّنا نعرض الجملة “ليس بعدد وليس سلسلة نصيّة ”. النّتيجة: Flask عبارة عن سلسلة نصيّة 2017 عبارة عن عدد None ليس بعدد وليس سلسلة نصيّة ختاما هكذا نكون قد تعرّفنا على ماهيّة الاختبارات في مُحرّك القوالب Jinja، وألقينا نظرة إلى بعض من أهمّ الاختبارات المبنيّة مُسبقا وعلى كيفيّة استخدامها في تطبيقات Flask أو أي مشروع بايثون آخر يعتمد على Jinja، سننتقل في الدّرس القادم إلى كيفيّة إنشاء اختبارات خاصّة بنا لاستعمالها في حالة لم تجد اختبارا يُلبي حاجتك في قائمة الاختبارات المبنيّة مُسبقا.
  16. مقدّمة بعد أن ألقينا نظرة على كيفيّة استخدام إطار العمل Flask لإنشاء تطبيقات ويب بسيطة بالاستعانة بقاعدة بيانات SQLite، سنبدأ هذه السّلسلة بالدّخول إلى عالم إضافات Flask التّي ستسهّل علينا الكثير من المهام كإعداد نظام لتسجيل الدّخول والخروج، والتّعامل مع نظام قاعدة بيانات، وكذلك التّحكم في تطوير التّطبيق عن طريق سطر الأوامر؛ وكذلك استعمال أدوات لتطوير تطبيق أكثر أمانا. سنتطرّق أيضا إلى بعض المفاهيم المتقدّمة كتشفير كلمات المرور وتهجير قواعد البيانات وغير ذلك. صحيح بأنّ هذه السّلسلة مطولة نوعا ما إلا أنّها ستكون شاملة لمُساعدتك على تطوير أي تطبيق يمر على خاطرك أو يطلبه منك أحد عملائك. وإن كنت جديدا إلى عالم تطوير الويب فستتعلّم من هذه السّلسلة ما يكفي لتبدأ مشوار العمل كمطوّر تطبيقات ويب سواء عن طريق إنجاز مشاريع لعملاء على منصّات العمل الحر كمنصّة مُستقل أو بناء مشاريعك الخاصّة. سنقوم في هذا الدّرس بالتّعرف على فكرة وبنية التّطبيق الذي سنبنيه في رحلتنا هذه. المُتطلّبات بالإضافة إلى أساسيّات إطار العمل Flask، ستحتاج كذلك إلى معرفة كيفيّة التّعامل مع نماذج HTML عبر مكتبة WTForms وإضافة Flask-WTF ويُمكنك الاطّلاع على سلسلة دروس خاصّة بهذا الموضوع على الأكاديميّة. سلاسل الدّروس التّي يجب عليك أن تطّلع عليها لفهم أفضل لهذه السّلسلة من الدّروس المُتقدّمة نوعا ما، عليك أن تطّلع وتطبّق سلاسل الدّروس التّاليّة: سلسلة مدخل إلى إطار العمل Flask درس إنشاء تطبيق لاختصار الرّوابط باستخدام إطار العمل Flask بجزئيه الأول و الثاني. سلسلة التّحقق من مُدخلات المُستخدم باستخدام مكتبة WTForms وإضافة Flask-WTF ستكون لغة جافاسكربت مُفيدة كذلك، ولا مفرّ من إتقانها إن كنت ترغب بأن تُصبح مُطور ويب أفضل، كما أنّني لن أشرح كل كبيرة وصغيرة عند التّعامل معها في التّطبيق، وهذا لأنّ الهدف من السّلسلة هو تعليمك كيفيّة إتقان التّعامل مع إطار العمل فلاسك وهذا بالطّبع لا يشمل شيفرة جافاسكربت. ستحتاج كذلك إلى تعلّم أداة Git لإدارة نُسَخ التّطبيق والتّعامل مع التّطبيق بعد نشره على منصّة Heroku. ما الذي ستتعلّمه من هذه السّلسلة؟ ستتعلم في هذه السّلسلة تقنيات متقدّمة لإدارة مشاريع تطبيقات الويب، وكيفيّة التّعامل مع قاعدة بيانات PostgreSQL وأداة SQLAlchemy لإدارة الجداول (إضافة، قراءة، تعديل وحذف البيانات)، وكذلك الطرق البسيطة لتهجير قواعد البيانات Database Migration وستتعرّف كذلك على طرق توزيع ملفّات التّطبيق على شكل وحدات وحزم لتنظيم أكثر. سنستخدم كذلك مكتبة WTForms لإدارة نماذج HTML وجعلها أكثر أمانًا للحماية من هجمات مُحتملة من المُخرّبين والمُخترقين. وسنعد كذلك نظامًا أكثر أمانًا لتسجيل الدّخول واستيثاق المُستخدمين، وسنتطرّق كذلك إلى كيفيّة نشر التّطبيق إلى منصّة Heroku لتربطه بعد ذلك باسم نطاق خاصّ إن أردت ذلك. ستتعلّم كذلك طريقة حماية كلمات المرور عن طريق تشفيرها في قاعدة البيانات باستعمال خوارزميّة Bcrypt للتّشفير، وذلك لتجنّب حفظها كنصّ مجرّد يسهل استخدامه من طرف المُخترق إذا وصل إلى قاعدة البيانات. وقد تعرّفنا سابقًا على كيفيّة استعمال الجلسات Sessions لإنشاء نظام تسجيل دخول وخروج بسيط، صحيح بأنّ ذلك النّظام يعمل بشكل جيّد، إلّا أنّه لا يعد آمنًا، ويُمكن أن يتعرّض تطبيقنا لهجمات كثيرة من قبل المُخترقين، ولحماية العضويات والبيانات في الموقع، سيتوجّب علينا أن نُدير الجلسات بطريقة أكثر تعقيدًا، لكنّ هناك إضافة لإطار العمل فلاسك لتبسيط هذا الأمر ولن نحتاج إلى العمل على ذلك بأنفسنا، وبالتّالي فسيُصبح تطبيقنا أكثر أمانًا دون عناء كبير، وكلّ هذا وأكثر سنتعلّمه بتفصيل أكبر في الدّروس القادمة. بنية التّطبيق التّطبيق الذي سنعمل عليه في هذه السّلسلة من الدّروس عبارة عن نظام إدارة محتوى ومنصّة للتّدوين الجماعي، ستكون هناك بعض الميّزات الفريدة وستكون هناك ميّزات أخرى مفقودة، إذا سبق لك وأن تعاملت مع أحد أنظمة التّدوين الأخرى كمنصّة ووردبريس WordPress، Blogger أو Tumblr أو حتى منصّة medium فستتمكّن بنهاية هذه السّلسلة من التّخلي عن هذه المنصّات وإنشاء منصّة بسيطة خاصّة بك لتكتب عليها، أو تستطيع تعديل المشروع بعد الانتهاء منه لتُضيف ميّزات أخرى أو تُطبّق فكرة فريدة لتطرح مشروعًا على أرض الواقع لتتمكّن من الاستفادة منه ماديّا إن أردت ذلك. سيتمكّن مُستخدمو المنصّة من إنشاء حساب خاصّ بهم لينشروا مقالاتهم التّي يكتبونها، إضافة المقالات ستتمّ عن طريق كتابتها بتنسيق الماركداون Markdown ومن ثمّ نشرها، سيتمكّن كلّ مُستخدم من الحصول على رابط خاصّ بمقالاته لنشرها للآخرين، سيتمكّن الكاتب من إضافة وسوم لمقالاته، وكذلك إدراجها ضمن قسم معيّن، لكل مقالة قسم للتّعليقات ليتمكّن المُستخدمون الآخرون من التّعليق على مقال معيّن. سيكون للتّطبيق نكهة اجتماعيّة، بحيث سيتمكن المُستخدمون من مُتابعة بعضهم البعض، وكذلك إضافة مقالات إلى المُفضّلة والإعجاب بها لنتمكّن من فرزها وترتيبها حسب الأكثر إعجابًا، الأكثر نشاطًا (أكبر عدد من التعليقات) وبعدها ستتمكّن من إضافة خاصيّات أخرى بنفسك باتّباع نفس الطّريقة، مثلًا بعد الانتهاء من التّطبيق ستتمكّن من تطبيق فكرة ترتيب المقالات حسب الأكثر إعجابًا لإضافة خاصيّات أخرى (الأكثر شعبيّة حسب عدد التّعليقات، الأكثر زيارة...). صحيح بأنّنا سنبني تطبيقًا مُعقّدا وكبيرًا، إلّا أنّني لن أشرح كلّ شيء بتفصيل ممل، سأشرح بالتّفصيل فقط الأفكار الجديدة وما تراه لأوّل مرّة حتى لا تكون هذه السّلسلة أطول من اللازم، خاصّة وأنّ بعض الأساليب ستكون مُشابهة جدّا لبعضها البعض، فمثلاً طريقة السّماح للمُستخدم بإضافة مقال خاص به مُشابهة جدّا لطريقة فرز المقالات حسب قسم مُعيّن (لكل مُستخدم مقالاته ولكلّ قسم مقالاته كذلك). سنضع للتّطبيق بعض الميّزات المُفيدة كنظام أرشفة لتأريخ كلّ مقال حسب تاريخ إضافته، كما سيكون لتعليقات المقال نظام مُشابه، وسنستغل نفس الطّريقة لتخصيص تاريخ انضمام للمُستخدم. في النّهاية، تذكّر بأنّ هذه السّلسلة من الدّروس ستكون تطبيقيّة بالدّرجة الأولى، وستتعلّم فيها كيفيّة بدء مشاريعك التّطويريّة الخاصّة بك بلغة بايثون وإطار فلاسك، أمّا كيفيّة الاستفادة القصوى منها فمعروفة، عليك تطبيق ما تعلّمته وتختبر مع إضافة ميّزات ولو لم أذكرها، وإن أردت معرفة معمّقة عن أي شيء تحدّثت عنه دون أن أفصّل، يُمكنك دائما العودة إلى التّوثيق الرّسمي للأدوات والمكتبات والإضافات المعنيّة بالأمر، وإن كان لك سؤال معيّن فلا تتردّد في طرحه على قسم الأسئلة والأجوبة على الأكاديميّة. هناك المزيد الدّروس التّي ستندرج تحت هذه السّلسلة لن تُغطي فقط ما قرَأتَهُ للتّو، بل أكثر من ذلك، فهدفي من كتابة سلسلة الدّروس هذه هو تغطيّة الفراغ الموجود في الويب العربي، إذ أنّ التّوثيقات الرّسميّة للمكتبات وأطر العمل المُستعملة حاليّا عادة ما تكون مكتوبة باللغة الانجليزيّة فقط، لذا بتوفير سلسلة دروس كهذه ستمتلك أكبر قدر مُمكن من المُساعدة لتتمكّن من العمل على تطبيقاتك الخاصّة دون أن تضطر إلى اتّباع سلسلة دروس أخرى أو الاطّلاع على التّوثيقات الرّسميّة التّي عادة ما تكون مُفيدة أكثر إذا ما أردت أن تُنجز شيئًا بسيطًا، وعلى العكس من ذلك، فكتابة تطبيق كامل (كما في هذه السّلسلة) طريقة أفضل للتّعلم. ختاما هذا المقال عبارة عن مُقدّمة بسيطة لما سنخوضه في قادم الدّروس، في الدّرس القادم، ستبدأ المُتعة مع استكشاف كيفيّة التّعامل مع ملفّات ومُجلّدات التّطبيقات الكبيرة والمُعقّدة بطريقة بسيطة.
  17. مقدّمة بعد أن تعرّفنا على بنية التّطبيق الذي سنبنيه سويا، سنبدأ بتهيئة وتنصيب بعض الحزم التّي سنعتمد عليها، وسنرى كيف يُمكننا تنظيم الملفّات لمرونة أكثر في التّعامل مع المشاريع الكبيرة، فعوضا عن وضع الشيفرة كاملة في ملفّ أو ملفّين، سنقوم باستعمال مبدأ المُخطّطات Blueprints الذي يوفّره لنا إطار العمل فلاسك لتنظيم التّطبيقات الأكثر تعقيدا. بنية التّطبيق المُعتادة سابقا، كنّا نستعمل ملفّا رئيسيّا واحدا ونقوم بتشغيله ليعمل الخادوم ونتمكّن من الوصول إلى التّطبيق عبر المُتصفّح من العنوان المُعتاد، وهكذا تكون جميع الموجّهات في ملفّ واحد، والملفّات السّاكنة في مجلّد واحد، وملفّات القوالب (ملفّات HTML) تكون جميعها في نفس المُجلّد. ملفّات التّطبيق المعتادة تكون على الشّكل التّالي: ├── app.py # ملفّ التّطبيق الرّئيسي ├── config.py # ملفّ الإعدادات ├── static # مجلّد الملفّات السّاكنة │ ├── css │ │ └── style.css │ ├── img │ │ └── logo.png │ └── js │ └── main.js ├── templates # مجلّد القوالب │ ├── index.html │ ├── posts.html مادام التّطبيق بسيطا فكلّ شيء سيكون على ما يرام، لكن ماذا لو كان التّطبيق يمتلك قسما لتسجيل المُستخدمين، عرض صفحاتهم الشّخصيّة، عرض المقالات، صفحات الأخطاء (مثل عدم تواجد صفحة أو خطأ بالخادوم …)، لوحة تحكّم للمُدير، لوحات التّحكم بأجزاء التّطبيق، واجهة برمجيّة للمُطوّرين API وغير ذلك من الخصائص التّي يتمتّع بها كل تطبيق كبير ذو وظائف مُتعدّدة. إن استعملت البنية المُشار إليها سابقا، ستواجه العديد من المشاكل، أولا سيكون الملفّ الرّئيسي مليئا بالموجّهات والدّوال، وقد يصل إلى آلاف الأسطر البرمجيّة ما سيجعل مهمّة الصّيانة وإضافة خصائص جديدة أمرا صعبا، كما أنّ ملفّات القوالب ستكون مجتمعة في مجلّد واحد، وإن كنت تعمل على تطوير التّطبيق مع فريق من المُطوّرين فسيجد كل شخص نفسك أمام ملفّات وشيفرات لا علاقة له بها، مثلا لو قُسّمت المهام بحيث يعمل كلّ مطوّر على قسم معيّن من التّطبيق، زيد هنا يعمل على نظام تسجيل للمُستخدمين، وعامر يعمل على إضافة قسم لعرض آخر التّعليقات المُضافة، وسعيد، مُصمّم الويب يعمل على تصميم صفحات أخطاء جميلة، وعبد القادر يعمل على لوحة تحكّم الإدارة، فكيف يُمكن لزيد أن يُعدّل الشيفرة التّي تقوم بتسجيل المُستخدمين دون المساس بالشّيفرة التّي تقوم بإعداد المقالات لعرضها على صفحة المقالات؟ المُخطّطات Blueprints المُخطّطات في فلاسك نظام لتجميع الموجّهات المُخصّصة لوظيفة مُعيّنة بعيدا عن المُوجّهات الأخرى، يُمكن كذلك تقسيم ملفّات القوالب والملفّات السّاكنة كذلك مع إتاحة خيار مُشاركة ملفّات مُحدّدة إن أردت ذلك. في هذا المشروع سنضع مجلّد قوالب لكلّ وظيفة، أمّا مُجلّد الملفّات السّاكنة فسيكون مُشتركا بين جميع القوالب، وهذا لأنّنا لن نحتاج إلى الكثير من ملفّات جافاسكريبت وملفّات css، وستكون الصّور مُشتركة كذلك. هناك نوعان معروفان من المُخطّطات، النّوع الأول يُقسّم ملفّات القوالب مع كلّ وظيفة ليكون كالتّالي: . ├── run.py ├── config.py ├── project │ ├── __init__.py │ ├── posts │ │ ├── __init__.py │ │ ├── templates │ │ │ ├── index.html │ │ │ ├── post.html │ │ │ ├── posts_by_category.html │ │ ├── views.py │ ├── static │ │ └── css │ │ └── style.css │ ├── templates │ │ ├── base.html │ └── users │ ├── __init__.py │ ├── templates │ │ ├── login.html │ │ └── register.html │ ├── views.py أمّا النّوع الثّاني فتجتمع فيه جميع ملفّات HTML في مُجلّد واحد كما يلي: . ├── run.py ├── config.py ├── project │ ├── __init__.py │ ├── posts │ │ ├── __init__.py │ │ ├── views.py │ └── users │ ├── __init__.py │ ├── views.py │ ├── templates │ │ ├── base.html │ │ ├── posts/ │ │ ├── users/ │ ├── static │ │ └── css │ │ └── style.css في المثالين السّابقين، لدينا مُخطّطان، مُخطّط خاصّ بالمقالات تحت مجلّد posts، وآخر خاصّ بالمُستخدمين في مجلّد users، ملفّ run.py مسؤول عن تشغيل التّطبيق وأمّا ملفّات __ini__.py فهي لجعل بايثون يفهم بأنّ كلّا من مجلّد المشروع project ومجلّدي المقالات والمُستخدمين عبارة عن حزم. أمّا ملفّا views.py فسيحتويان على الموجّهات المعنيّة بكلّ وظيفة، فمثلا مُوجّه عرض المقالات “/posts” سيكون في ملفّ posts/views.py أمّا موجّه تسجيل دخول المُستخدمين /login فسيكون في ملفّ users/views.py. لإخبار فلاسك بأنّ مجلّدي posts و users عبارة عن مُخطّطات، سنعرّف أولا كائنًا من صنف Blueprint في كل من ملفّات __init__.py في المُجلّدين، وبعدها سنقوم بتسجيلهما مع تطبيقنا الذي نُعرّفه في ملفّ __init__.py الأساسي -وهو المُتواجد في مجلّد project-. لك حريّة استخدام أي نمط تُريده، ففي النّهاية إطار فلاسك مُصمّم ليُوفّر لك كامل الحريّة في طريقة تنظيمك لملفّات التّطبيق، لكن اتّباع نمط مُتعارف عليه أفضل في مُعظم الحالات. سنستخدم في مشروعنا النّمط الأول، أي أنّ ملفّات HTML ستكون منفصلة حسب الوظيفة. بايثون 3 سنستعمل في إنشائنا لهذا التّطبيق النّسخة 3 من لغة بايثون، لن يتغيّر الكثير، فالاختلاف بين Python 2 و Python 3 بسيط جدا، وسأقوم بذكر أهم الاختلافات أثناء بناء التّطبيق في الدّروس القادمة. إذا كنت لا تزال تعتمد على بايثون 2 فأنصحك بأن تنتقل إلى بايثون 3 لأنّ الدّعم لهذه الأخيرة قد أصبح كبيرا ومُعظم مُطوري بايثون يستعملونها الآن، كما أنّها أحدث نُسخة. إذا أردت أن تتعلم كيفيّة تجهيز بايثون 3 على نظامك، يُمكنك اتّباع هذا الدّرس. تطبيق كلمة بما أنّ مشروعنا عبارة عن نظام لإدارة المُحتوى، قرّرت تسميّته “كلمة”، جميع الملفّات والمجلّدات الخاصّة بالتّطبيق ستكون بداخل مجلّد باسمkalima، لذا عليك إنشاؤه الآن في المكان المُفضّل لديك، المهم أن تعرف كيفيّة الوصول إليه عن طريق سطر الأوامر. في البداية، سننشئ الملفّات التّاليّة: . ├── run.py ├── project │ ├── __init__.py │ ├── posts │ │ ├── __init__.py │ │ ├── templates │ │ │ ├── posts.html │ │ ├── views.py │ ├── static │ │ └── css │ │ └── style.css │ ├── templates │ │ ├── base.html │ └── users │ ├── __init__.py │ ├── templates │ │ ├── users.html │ │ ├── login.html │ ├── views.py يُمكنك إمّا إنشاء المُجلّدات والملفّات يدويّا أو عبر نسخ الشيفرة التّالية وحفظها في ملفّ باسم create_app.py لإنشاء الملفّات والمُجلّدات باستعمال لغة بايثون تلقائيّا. import os import sys def mkapp(app_name, blueprints): dirs = ['{}', '{}/static', '{}/static/css', '{}/templates'] static_files = ['{s}/css/style.css'] templates_files = ['{t}/index.html', '{t}/base.html'] for d in dirs: os.mkdir(d.format(app_name)) open(app_name + '/' + "__init__.py", "w").close() # project/__init__.py for b in blueprints: os.mkdir(app_name + '/' + b) # project/posts os.mkdir(app_name + '/' + b + '/templates') # project/posts/templates open(app_name + '/' + b + '/' + "views.py", "w").close() # project/posts/views.py open(app_name + '/' + b + '/' + "__init__.py", "w").close() #project/posts/__init__.py open(app_name + '/' + b + '/templates/' + b + ".html", "w").close() # project/posts/templates/index.html for sf in static_files: static_file = app_name + '/' + sf.format(s='static') # project/static open(static_file, 'w').close() for tf in templates_files: templates_file = app_name + '/' + tf.format(t='templates') # project/templates open(templates_file, 'w').close() if __name__ == '__main__': app = sys.argv[1] blueprints = sys.argv[2:] mkapp(app, blueprints) يُمكنك تنفيذ السكربت بالأمر التّالي: $ python3 create_app.py project posts users بعدها أنشئ ملفّ login.html في مجلّد project/users/templates لأنّ السكربت لن يقوم بإنشائه. عليك كذلك إنشاء ملفّي run.py وconfig.py داخل مُجلّد kalima، الملفّ الأول مسؤول عن تشغيل الخادوم/ أمّا الملفّ الثّاني فمسؤول عن إعدادات التّطبيق. خاتمة تعرّفنا في هذا الدّرس على بنية التّطبيق الذي سنبنيه وكيفيّة توزيع ملفّاته إذ أنشأنا المُجلّدات والملفّات التّي ستكون مسؤولة عن الجانب العملي للتّطبيق، في الدّرس القادم، سنبدأ بتجهيز البيئة البرمجيّة وتنصيب أهم الحزم التّي سنحتاج إليها للبدء بتطوير التّطبيق.
  18. flask_cms

    مُقدّمة بعد أن تعرّفنا على بنية التّطبيق الذي سنبنيه سويًّا باستخدام إطار العمل Flask ولغة Python، سننتقل الآن إلى العمل على تجهيز بيئة وهميّة لبايثون 3 لنقوم بتنصيب إطار Flask عليها ونبدأ بتطوير التّطبيق بمعزل عن النّظام المتواجد في الحاسوب، بعد تهيئة البيئة البرمجيّة، سنتعرّف على كيفيّة استغلال مبدأ المُخطّطات Blueprints التّي يُوفرّها لنا إطار العمل Flask لتوزيع أفضل لملفّات ومُوجّهات التّطبيق لعزل كل جزء وظيفيّ عن الأجزاء الأخرى وبالتّالي تسهيل تطوير خصائص وميّزات وكذلك تقسيم المُهمّات على أفراد الفريق البرمجي في حالة كنت تعمل على تطبيق مع أكثر من مُطوّر. إنشاء بيئة وهميّة بأداة Pyvenv أولا، سنحتاج إلى إنشاء بيئة وهميّة لعزل اعتماديات التّطبيق عن اعتماديات النّظام، وبما أنّ لديك خبرة في لغة بايثون، فلا بد بأنّك تُفكّر في استخدام أداة virtualenv، يُمكنك استخدامها إن أردت، لكنّنا سنستعمل بايثون 3 في تطويرنا لهذا التّطبيق، وإذا اتّبعت الدّرس الذي أشرت إليه في الدّرس السّابق، فستعلم بأنّه من المُفضّل استعمال أداة pyvenv للتأكّد من أنّ البيئة الوهميّة ستكون خاصّة بلغة بايثون 3 وليس بايثون 2. اتّبع الطّريقة التّقليديّة لإنشاء بيئة وهميّة باستعمال أداة pyvenv عبر السّطر التّالي: pyvenv venv إذا كنت تستخدم نظام Windows فقد لا يعمل السّطر أعلاه، لكن يُمكنك استخدام الأمر التّالي عوضا عنه: python -m venv فقط تأكّد من أنّ الأمر python يوصلك إلى مُفسّر بايثون 3 وليس مُفسّر بايثون 2. بعد انتهاء الأمر من التّنفيذ، قم بتفعيل البيئة الوهميّة بالسّطر التّالي: source venv/bin/activate بعد تفعيل البيئة الوهميّة، قم بتنصيب إطار العمل Flask باستخدام أداة pip بالسّطر التّالي: pip install Flask إذا كان السّطر ما قبل الأخير من المُخرجات كما يلي، فهذا يعني بأنّ كل شيء بخير: Successfully installed Flask itsdangerous Jinja2 Werkzeug click MarkupSafe طريقة العمل بما أنّنا أصبحنا نتعامل مع الكثير من الملفّات، عليك أن تعرف أي شيفرة ستكون في أي ملف، ولهذا سأقوم بوضع مسار الملف في أعلى الشّيفرة على شكل تعليق. وفي معظم الأحيان، لن أضع محتويات الملف كاملًا، بل سأضع الشيفرة الجديدة مُحاطة بسطرين قديمين من الأعلى والأسفل لتُدرك أي مكان يجب عليك أن تضع به الشّيفرة. مثال على الشّيفرة سيكون كالآتي: # project/users/views.py @users.route('/login') def login(): . . . login_user(user) return redirect(url_for('users.posts_by_author')) لا تقلق إن لم تفهم الأجزاء الجديدة عليك من الشّيفرة، فهي مُجرّد مثال بسيط على كيفيّة استخدام مُوجّه لمُخطّط وكيفيّة الرّبط بين الموجّهات. إنشاء المُخطّطات لدينا الآن مُخطّطان في تطبيقنا، واحد في مجلد المُستخدمين والآخر في مُجلّد المقالات. سنقوم أولا بإنشاء المُخطّط الخاصّ بالمُستخدمين في ملفّ views.py المتواجد داخل المُجلّد users كما يلي: # project/users/views.py from flask import Blueprint users = Blueprint('users', __name__, template_folder='templates', url_prefix='/users') في السّطر الأول، نستدعي Blueprint من حزمة flask، ونقوم بإنشاء كائن منها كما كنّا نقوم بإنشاء كائن app من الصّنف Flask في الماضي. المعامل الأول عبارة عن اسم المُخطّط، والمعامل الثّالث هدفه إخبار فلاسك بأنّ مُجلّد القوالب سيكون في نفس مُجلّد المُخطّط، وسيكون باسم templates ويُمكنك تغيير هذا الاسم إلى ما تشاء، لكنّنا لن نقوم بذلك، ومن المُفضّل اتّباع المُمارسات الشّائعة في مثل هذه الأمور. أمّا مُعامل url_prefix فالهدف منه وضع موجّه رئيسي قبل الموجّه المعرّف. مثلًا لتعريف الموجّه الرّئيسي / سيكون كالآتي: @users.route('/') # /users ولتعريف موجّه التّسجيل: @users.route('/register') # /users/register وكما تُلاحظ، لا أحتاج إلى تعريف /users في بداية كل مُوجّه. لو لم نُخصّص مُجلّدًا للقوالب، لكان علينا جمع جميع ملفّات HTML في مجلّد project/templates. هناك كذلك خيار لتفريق مجلّد الملفّات السّاكنة بنفس الطّريقة: users = Blueprint('users', __name__, template_folder='templates', static_folder='static', url_prefix='/users') ولأنّنا سنجمع جميع الملفّات السّاكنة في مُجلّد project/static فهذا الخيار لن يكون مُفيدًا لنا. يُمكنك الآن إنشاء المُخطّط الخاصّ بالمقالات بنفس الطّريقة. # project/posts/views.py from flask import Blueprint posts = Blueprint('posts', __name__, template_folder='templates', url_prefix='/posts') تسجيل المُخطّطات مع التّطبيق الرّئيسي بعد أن قُمنا بإنشاء مُخطّطات، حان الوقت لإخبار إطار فلاسك بأنّ هذه المُخّططات تابعة للتّطبيق، وطريقة القيام بذلك بسيطة. افتح الملف project/__init__.py وضع به ما يلي: # project/__init__.py from flask import Flask app = Flask(__name__) from project.posts.views import posts from project.users.views import users app.register_blueprint(posts) app.register_blueprint(users) @app.route('/') def index(): return 'Project Index' السّطران الأولان معروف بأنّهما لإنشاء التّطبيق، أمّا الاستدعاءات فهي لاستدعاء المُتغيّرين اللذين يحملان المُخطّطان، أمّا الأسطر الأخيرة فهي لتسجيل المُخطّطات التّي استدعيناها. أمّا الموجّه الذي عرّفته في الأسفل فهو ليتمكّن المُستخدم من الوصول إلى الصّفحة الرّئيسية دون الحصول على خطأ من نوع 404. ذلك لأنّ الموجّهات التّي سنعرّفها في المُخطّطات ستكون كما يلي: /posts /posts/new /users /users/register . . . لذا إن دخل المُستخدم إلى العنوان 127.0.0.1:5000 فلن يجد شيئا لأنّنا لم نُحدّد الموجّه الرّئيسي /، وهذا هو السّبب الذي جعلنا نُضيف المُوجّه بملفّ __init__.py في مجلّد المشروع project. إنشاء مُوجّهين رئيسيّين قلنا سابقًا بأنّ المُوجّهات ستكون داخل ملفّات views.py فإن أردنا أن نعرّف مثلًا موجّهًا للمقالات الرّئيسية، فسنقوم بذلك في ملّف posts/views.py أمّا الخاص بالمُستخدمين فسيكون في ملفّ users/views.py وهكذا. سنقوم الآن بإنشاء مُوجّه رئيسي لكل مُخطّط. أولا موجّه /posts/ سيكون كما يلي (ضعه مُباشرة بعد سطر تعريف المُتغيّر posts): # project/posts/views.py @posts.route('/') # 127.0.0.1:5000/posts def index(): return 'Posts Index' أمّا المُوجّه الخاصّ بالمُستخدمين فسيكون كما يلي: # project/users/views.py @users.route('/') # 127.0.0.1:5000/users def index(): return 'Users Index' وبهذه التّغييرات سيُصبح الملفّ views.py الخاصّ بالمُستخدمين كما يلي: from flask import Blueprint users = Blueprint('users', __name__, template_folder='templates', url_prefix='/users') @users.route('/') # 127.0.0.1:5000/users def index(): return 'Users Index' وملفّ views.py الخاص بالمقالات سيكون كالآتي: from flask import Blueprint posts = Blueprint('posts', __name__, template_folder='templates', url_prefix='/posts') @posts.route('/') # 127.0.0.1:5000/posts def index(): return 'Posts Index' تشغيل الخادوم بعد أن قمنا بتسجيل المُخطّطات وأنهينا أساسيّات التّطبيق، علينا أن نقوم بتشغيل الخادوم لنتمكّن من مُعاينة ما قُمنا به، ولتشغيل التّطبيق، سنضع السّطر المسؤول عن الأمر في ملفّ باسم run.py في مجلّد kalima. محتويات الملفّ ستكون كالتّالي: # run.py from project import app app.run(debug=True) كما تُلاحظ، استدعينا الكائن app الذي سبق وأن عرّفناه في ملفّ project/__init__.py بعدها نقوم بتشغيل الخادوم بالطّريقة المُعتادة مع خيار تصحيح الأخطاء. بعد حفظ ملفّ run.py يُمكنك تشغيله عبر سطر الأوامر: $ python run.py ستُلاحظ بأنّ الخادوم قد بدأ بالاشتغال كما العادة، لكنّ طريقة تشغيل الخادوم بهذه الطّريقة غير منصوح بها لأنّ النّسخة رقم 0.11 من Flask التّي أطلقت في أواخر عام 2016 جاءت بطريقة جديدة لتشغيل الخادوم عبر تنفيذ الأمر flask run. لكن الأمر flask run لن يعمل إلّا بعد تحديد اسم الحزمة أو الوحدة التّي يتواجد بها تطبيق Flask، وذلك عبر تعريف مُتغيّر بيئة باسم FLASK_APP وإعطائه القيمة حسب مكان تواجد تطبيق Flask الخاص بك. لتعريف مُتغيّر البيئة يُمكنك تنفيذ الأمر التّالي من الطّرفيّة بعد التّأكد من أنّ البيئة الوهميّة لديك مُفعّلة: $ export FLASK_APP=app.py في نظام Windows يجب عليك استخدام الكلمة المفتاحيّة set عوضا عن export: > set FLASK_APP=app.py في المثال أعلاه، نفترض بأنّ شيفرة تطبيق Flask مُتواجدة في الملفّ app.py. بعد تعريف مُتغيّر البيئة يُمكنك تشغيل الخادوم عبر تنفيذ الأمر التّالي: flask run عوضًا عن الأمر: python app.py في مشروعنا، تطبيق Flask مُتواجد داخل ملفّ __init__.py في مُجلّد project لذا فطريقة تعيين مُتغيّر البيئة سيكون كالتّالي: $ export FLASK_APP=project/__init__.py بعد تحديد مُتغيّر البيئة، يُمكنك تجربة تشغيل الخادوم: flask run بعد تنفيذك للأمر أعلاه، يجب أن تحصل على مُخرج كما يلي: * Serving Flask app "project" * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) لتفعيل مُصحّح الأخطاء، كل ما عليك فعله هو تعريف مُتغيّر البيئة FLASK_DEBUG وتعيين القيمة 1 له: export FLASK_DEBUG=1 مُلاحظة: أذكّر بأنّ تفعيل مُصحّح الأخطاء في بيئة الإنتاج أمر خطير، ولا يجب عليك أبدًا أن تقوم بذلك حتى ولو كنت مُتأكّدًا من أنّ تطبيقك خال من الأخطاء، وعليك أن تُدرك بأنّ مُصحّح الأخطاء مصمّم لمُساعدتك على تطوير واختبار التّطبيق وليس لتفعيله بعد نشر التّطبيق على الأنترنت. ومُجدّدًا، استبدل export بالكلمة set إذا كنت تستخدم نظام Windows. بعد تشغيل الخادوم مع وضع تصحيح الأخطاء، فسيكون المُخرج مُشابها لما يلي: * Serving Flask app "project" * Forcing debug mode on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger pin code: 573-756-626 وإن أردت أن يصل الآخرون في شبكتك إلى موقعك، تستطيع إضافة --host=0.0.0.0 إلى أمر تشغيل الخادوم كما يلي: flask run --host=0.0.0.0 بعد تشغيل الخادوم ستتمكّن من الوصول إلى الموجّهات الثلاثة التّي سبق وأن عرّفناها. 127.0.0.1:5000/ 127.0.0.1:5000/posts 127.0.0.1:5000/users وستُلاحظ النّتائج: Project Index Posts Index Users Index خاتمة تعرّفنا في الدّرس على كيفيّة إنشاء مُخطّطات وتسجيلها لمرونة أكثر في التّطوير ولإدارة أفضل للتّطبيقات الكبيرة، وتعرّفنا كذلك على كيفيّة تشغيله وكيفيّة إضافة مُوجّه لكل مُخطّط، في الدّرس القادم سنتعرّف على كيفيّة التّعامل مع قوالب HTML وكيفيّة الرّبط بين مُختلف موجّهات التّطبيق.
  19. مُقدّمة: بعد أن قمنا بتنظيم ملفّات التّطبيق على حسب الوظيفة باستعمال خاصيّة المُخطّطات، أصبح لدينا اختلاف بسيط في كيفيّة التّعامل مع ملفّات العرض التّي تحتوي على شيفرات HTML وشيفرات مُحرّك القوالب Jinja، وسيكون هناك اختلاف بسيط أيضا في طريقة استخدام الدّالة url_for للرّبط بين المُوجّهات. إنشاء صفحات HTML بما أنّنا أنشأنا ثلاثة مجلّدات باسم templates لتعدّد أغراضها، فسيتوجّب علينا أن نفهم أولا كل مجلّد ووظيفته. مسارات المُجلّدات التّي أنشأناها سابقا هي كالآتي: project/templates/ project/posts/templates/ project/users/templates/ المسار الأول عبارة عن مجلّد رئيسي للقوالب، يُمكنك أن تعتبره مُشتركا مع القوالب الأخرى، فمثلا يُمكن لكل من ملفّات المقالات والمُستخدمين أن يرثوا من ملفّ base.html كما سنوضّح بعد قليل، كما أنّ أي موجّه يُعرّف داخل الملفّ الرّئيسي project/__init__.py له الحق في تقديم الملفّات المُتواجدة في مجلّد القوالب الرّئيسي مُباشرة. أي أنّك تستطيع تعديل الموجّه الرّئيسي ليُقدّم القالب index.html المتواجد داخل مجلّد project/templates كالآتي: # project/__init__.py from flask import Flask, render_template #... #... @app.route('/') # 127.0.0.1:5000/ def index(): return render_template('index.html') # render project/templates/index.html لا تنس بأنّنا استردنا أولا الدّالة render_template ثمّ استدعيناها في المُوجّه لتقديم الملفّ index.html. بنفس الطّريقة سنقوم بتقديم كلّ من ملفّي posts.html الخاصّ بالمقالات و users.html الخاص بالمُستخدمين. حدّث المُوجّه في مُخطّط المقالات ليُصبح كما يلي: # project/posts/views.py from flask import Blueprint, render_template @posts.route('/') # 127.0.0.1:5000/posts def index(): return render_template('posts.html') # render project/posts/templates/posts.html وهكذا سيكون الموجّه الجديد في المُخطّطات الخاصّة بالمُستخدمين: # project/users/views.py from flask import Blueprint, render_template @users.route('/') # 127.0.0.1:5000/users def index(): return render_template('users.html') # render project/users/templates/users.html هكذا إن طلب الزّائر أي صفحة من الصّفحات الثّلاث فسيُقدّمُ ملفّ HTML المُناسب. استخدام إطار Bootsrap لتنسيق الصّفحات الهدف من هذه السّلسلة هو التّعريف بكيفيّة إنشاء تطبيقات ويب ديناميكيّة بلغة بايثون وإطار فلاسك ولن أتطرّق إلى لغات أساسيّة مثل HTML و css وJavascript، ولأنّ تطبيقات الويب في يومنا هذا أصبحت تعتمد كثيرا على المظهر الجميل والاستخدام المرن باستعمال كل من لغتي css وjavascript، فمن الضّروري أن نستعمل إطار عمل لسد هذا الفراغ وإتاحة تصميم أنيق لتطبيقنا. سنستخدم في هذا المشروع إطار bootsrap 3 الذي يُمكنك تحميله من الموقع الرّسمي وفك الضّغط عن ملفّ zip داخل مجلّد project/static، سأعتمد كذلك على مشروع Bootsrap-rtl لتعريب وتوجيه التّنسيق من اليمين إلى اليسار وسأضعه في مجلّد project/static/css كما سأضع مكتبة jquery داخل مجلّد project/static/css/bootstrap لذا تأكّد من أنّك قد جهّزت كلّ شيء قبل أن تبدأ في استدعاء الملفّات السّاكنة. بعد تجهيز مُجلّد static ستكون المُجلّدات والملفّات بداخله كما يلي: project/static/ ├── bootstrap │ ├── css │ │ └── bootstrap.min.css │ └── js │ ├── bootstrap.min.js │ └── jquery.js └── css ├── bootstrap-rtl.min.css └── style.css قد تكون هناك ملفّات إضافيّة حسب ما قُمت بتنزيله عندما نزّلت إطار العمل Bootstrap، لكنّ الملفّات أعلاه هي الأهم وهي التّي سنحتاج إليها حاليّا. إذا أردت إضافة ملفّات Javascript أخرى، فيُمكنك إمّا أن تضعها داخل مُجلّد js المُتواجد داخل المُجلّد bootstrap أو أن تُنشئ مُجلّدا جديدا باسم js داخل مُجلّد الملفّات السّاكنة static. الوراثة في قوالب Jinja2 لنقل بأنّنا نُريد عرض جملة واحدة في كل ملف من ملفات HTML الثلاثة. يُمكن أن أقوم بما يلي في كل ملفّ: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> Hello </body> </html> مع تعويض Hello بالجملة التّي ارغب في عرضها عند طلب كل موجّه. هذه الطّريقة تعمل بشكل جيّد، لكن ألا تُلاحظ بأنّنا سنُكرّر نفس الأسطر في كلّ ملف؟ عوضا عن تكرار الأسطر في كلّ ملف يُمكننا استعمال مبدأ الوراثة في مجرّك القوالب Jinja. وللاستفادة من هذا المبدأ في المثال السّابق، يُمكن أن نكتب الأسطر التّي تتكرّر في كل ملف في ملفّ رئيسي واحد يُعتبر القاعدة لجميع الملفّات الأخرى، وهذا هو دور ملفّ project/templates/base.html الذي أنشأناه سابقا، ففيه يُمكننا وضع الأسطر السّابقة مع تخصيص المكان الذي سنستطيع كتابة الجملة الخاصّة بكل ملف فيه. افتح ملفّ base.html وضع فيه ما يلي: {# project/templates/base.html #} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %}</title> <script src="{{ url_for("static", filename="bootstrap/js/jquery.js") }}"></script> <script src="{{ url_for("static", filename="bootstrap/js/bootstrap.min.js") }}"></script> <link rel="stylesheet" href="{{ url_for("static", filename="bootstrap/css/bootstrap.min.css") }}"> <link rel="stylesheet" href="{{ url_for("static", filename="css/style.css") }}"> <link rel="stylesheet" href="{{ url_for("static", filename="css/bootstrap-rtl.min.css") }}"> </head> <body> {% block content %} {% endblock %} </body> </html> احفظ الملف وأغلقه. لاحظ بأنّنا قمنا باستدعاء جميع الملفّات التّي سنحتاج إليها للتّنسيق وإضافة ميّزات أخرى بلغة Javascript، وقُمنا كذلك بوضع كتلة Block باسم title لتعويضها بالعنوان في كل ملفّ، أمّا كتلة content فهي التّي ستُعوّضُ بالمحتوى (كلمة Hello في مثالنا السّابق). يُمكنك إضافة ملفّات أخرى إلى مُجلّد static، لكن لا تنس أن تقوم باستيرادها في ملفّ base.html لكي تعمل في جميع الصّفحات. بعد تجهيز الملفّ base.html الذي سيُشكّل قاعدة للملفّات الأخرى، سنُنشئ الآن ثلاثة ملفّات HTML رئيسيّة، وهي كما يلي: ملفّ HTML للصّفحة الرّئيسية (مساره project/templates/index.html ). ملفّ HTML للصّفحة الرّئيسية للمُستخدمين (مساره project/users/templates/users.html). ملفّ المقالات الرّئيسي (مساره project/posts/templates/posts.html). لملء الملفّات، ضع الشّيفرة التّاليّة في كلّ ملفّ يُمكنك الاستعانة بالمسار المُشار إليه في التّعليق أعلى الشّيفرة للوصول إلى الملفّ بشكل أسرع. ملفّ الصّفحة الرّئيسية: {# project/templates/index.html #} {% extends 'base.html' %} {% block title %} كلمة – الصّفحة الرّئيسية {% endblock %} {% block content %} <h1> مرحبا بك في تطبيق كلمة </h1> {% endblock %} ملفّ الصّفحة الرّئيسية للمُستخدمين: {# project/users/templates/users.html #} {% extends 'base.html' %} {% block title %} كلمة – المُستخدمون {% endblock %} {% block content %} <h1> الصّفحة الرّئيسية للمُستخدمين </h1> {% endblock %} ملفّ الصّفحة الرّئيسية للمقالات: {# project/posts/templates/posts.html #} {% extends 'base.html' %} {% block title %} كلمة – المقالات {% endblock %} {% block content %} <h1> الصّفحة الرّئيسية للمقالات </h1> {% endblock %} كما ترى عوضا عن تكرار الكثير من شيفرات HTML قمنا ببساطة بإنشاء ملفّ واحد والوراثة منه باستخدام الجملة extends 'base.html' وفي كلّ مكان نضع المحتوى المناسب، وبهذه الطّريقة سنمتلك طريقة ديناميكية لتحديد عنوان ومحتوى كلّ موجّه مرتبط بقالب معيّن، بالإضافة إلى أنّ الملفّات السّاكنة مثل ملفّاتcss و js ستكون مُتاحة في جميع الملفّات التّي ترث من الملّف الرّئيسي base.html. مُلاحظة: في بعض المشاريع المكتوبة بلغة بايثون وإطار العمل فلاسك، يُمكن أن يُسمّى الملفّ المُشترك باسم layout.html عوضا عن base.html، لكنّ المبدأ هو نفسه. الآن إن عدت إلى الموجّهات السّابقة، فستُلاحظ بأنّ التّنسيق ومكوّنات كلّ صفحة قد تغيّرت. الرّبط بين المُوجّهات باستخدام الدّالة url_for بعد أن تعرّفنا على كيفيّة تقديم قوالب HTML، أصبح بإمكاننا عرض صفحات للمُستخدم حسب المُوجّه المطلوب، وبقي لنا التّعرف على كيفيّة الرّبط بين هذه الموجّهات. للرّبط بين موجّهات المُخطّطات باستخدام الدّالة url_for، نقوم أولا بوضع اسم المُخطّط ثمّ اسم الدّالة التّي يتم استدعاؤها عند الوصول إلى المُوجّه، وبما أنّنا سمينا هذه الدّوال باسم index فللرّبط بينها يُمكنك أن تقوم بشيء مُشابه لما يلي: url_for('index') # / url_for('posts.index') # /posts url_for('users.index') # /users لو كانت الدّالة تقبل مُعاملا لتوجّب علينا تمرير قيمته كالآتي: url_for('posts.post_by_id', post_id = 1) للرّبط بين الموجّهات الثّلاثة التّي سبق وأن أنشأناها سنُضيف ما يلي مُباشرة بعد وسم h1 في كل ملفّ من ملفّات HTML حسب المسار في بداية الشّيفرة. الرّوابط في الصّفحة الرّئيسية: {# project/templates/index.html #} <a href="{{ url_for("posts.index") }}"> اضغط هنا للوصول إلى صفحة المقالات </a> <br> <a href="{{ url_for("users.index") }}">اضغط هنا للوصول إلى صفحة المُستخدمين</a> رابط العودة إلى الصّفحة الرّئيسية في صفحة المقالات: {# project/posts/templates/posts.html #} <a href="{{ url_for("index") }}">اضغط هنا للعودة إلى الصّفحة الرّئيسيّة</a> رابط العودة إلى الصّفحة الرّئيسية في صفحة المُستخدمين: {# project/users/templates/users.html #} <a href="{{ url_for("index") }}">اضغط هنا للعودة إلى الصّفحة الرّئيسيّة</a> بعد حفظ الملفّات، ستجد بأنّ الصّفحة الرّئيسية أصبحت تحتوي على رابط لكلّ من صفحتي المقالات والمُستخدمين مع رابط للعودة إلى الصّفحة الرّئيسية في كل صفحة من الصّفحتين. الرّبط بين المُوجّهات مع تمرير مُعاملات للدالّة التّابعة لكل موجّه. لفهم مبدأ المعاملات أكثر، سنقوم بإضافة موجّه للوصول إلى مقال برقم مُعرّفه في ملفّ project/posts/views.py كما يلي: # project/posts/views.py #.. #.. @posts.route('/<int:id>') def post_by_id(id): return render_template('post.html', id = id) لاحظ بأنّنا خصّصنا المعامل id من نوع int أي عدد صحيح، فإن لم تكن قيمة المُعامل عند الوصول إلى الرّابط عددا صحيحا فإنّ ذلك سيُسبّب خطأ من نوع 404. بمعنى أدق، الرّابط /posts/1 صحيح، أمّا /posts/abc فسيرجع الخطأ ذو الرّقم 404. سنتعرّف على كيفيّة التّعامل مع هذه الأخطاء في درس قادم. سنضع ما يلي في ملفّ post.html الذي قدّمناه: {% extends 'base.html' %} {% block title %} كلمة – المقال ذو المُعرّف {{ id }} {% endblock %} {% block content %} <h1> صفحة المقال الواحد ذو رقم المُعرّف {{id}} </h1> <a href="{{ url_for("index") }}">اضغط هنا للعودة إلى الصّفحة الرّئيسيّة</a> {% endblock %} لنستخدم الآن الدّالة url_for داخل ملفّ posts.html لضرب مثال على كيفيّة تمرير مُعاملات إلى الدّالة. أضف ما يلي إلى ملفّ posts.html مُباشرة بعد الوسم <h1>: <a href="{{url_for("posts.post_by_id", id=1)}}"> <h2> رابط المقال الأول </h2> </a> <a href="{{url_for("posts.post_by_id", id=2)}}"> <h2> رابط المقال الثّاني </h2> </a> الآن، إن دخلت إلى صفحة المقالات فستلاحظ رابطين لمقالين وهميّين، الأول رقم معرّفه 1 والآخر رقم مُعرّفه 2، ورابط المقالين كالتّالي: http://127.0.0.1:5000/posts/1 http://127.0.0.1:5000/posts/2 يُمكنك كذلك زيارة صفحات أخرى عبر تغيير قيمة رقم المُعرّف إلى أي عدد صحيح، والتّالي بعض الأمثلة: http://127.0.0.1:5000/posts/99 http://127.0.0.1:5000/posts/232 في المقالات القادمة سوف نعوّض جملة “صفحة المقال ذي المُعرّف رقم * ” بعنوان المقال وسنعرض محتواه وقسمه أسفل العنوان مع الوسوم الخاصّة بالمقال، وذلك بجلب كل مقال حسب رقم مُعرّفه في قاعدة البيانات. خاتمة تعرّفنا في هذا الدّرس على جزء آخر من أساسيّات تطوير تطبيقات ويب أكثر تعقيدا وذات مهام مُتعدّدة، هذا الدّرس يُمثّل قسما صغيرا فقط ممّا يجب عليك معرفته حول كيفيّة التّعامل مع قوالب HTML ومحرّكها Jinja الذي يُرافق إطار العمل Flask افتراضيّا، لكنّك ستجده في مشاريع بايثون أخرى تتعلّق بتطوير الويب وبعض المجالات الأخرى ولو لم يكن إطار Flask حاضرا، لذا فتعلّم أساسيّات Jinja سيُخوّلك لتعلّم أساسيّات أطر عمل أخرى ومُختلف الأدوات التّي تعتمد عليه بشكل أسرع.
  20. مُقدّمة تعلّمنا في الدّروس السّابقة كيفيّة إدارة مشروع مكتوب بإطار العمل فلاسك، وقد تعرّفنا في الدّرس السّابق على كيفيّة التّعامل مع قوالب HTML، في هذا الدّرس سنُكمل هذا الجزء من تطوير الويب بتعلّم أساسيّات مُحرّك القوالب Jinja الذي يأتي مُرفقا مع إطار العمل فلاسك افتراضيّا، والذّي تُعتبر أساسيّاته أمرا مُهمّا لكونه يُستعمل في العديد من مشاريع بايثون أخرى بالإضافة إلى إطار العمل فلاسك. الدّوال في Jinja يُمكننا تعريف دوال لتحمل شيفرة معيّنة مع معاملات على Jinja لإعادة استعمالها وتحقيق مبدأ DRY أو Don’t Repeat Yourself وترجمته إلى اللغة العربيّة هي “لا تكرّر نفسك” ويعني بأنّه لا يجب عليك تكرار كتابة نفس الشّيفرة، وكلّما وجدت نفسك تُكرّرها توقّف وفكّر في إيجاد حل لتجنّب هذا التّكرار، إما بوضعها في دالّة أو شيء من هذا القبيل لتسهل عليك إعادة استعمالها. في Jinja مبدأ الدّوال موجود في ما يُسمى بالماكرو macro ويُمكن تعريف واحدة كما يلي: {% macro name(param1, param2) %} . . . {% endmacro %} كما تُلاحظ، الأمر شبيه بكيفيّة إنشاء الدّوال في لغة بايثون، لاحظ فقط كيف نُنهي الجزء الخاص بالدّالة بكلمة endmacro. يُمكننا استدعاء الماكرو في مكان آخر من القالب كما نعرض قيمة مُتغيّر معيّن: {{ name('pram1_value', 'pram2_value' }} يُمكننا كذلك تعريف الماكرو دون أية مُعاملات. ومن الشّائع أن توضع في ملف باسم _helpers.html في مجلّد templates الرّئيسي، وبعدها لاستخدامه يجب استيراده في أعلى الملف كما يلي: {% from "_helpers.html" import macro_name %} مع استبدال macro_name باسم الماكرو، تأكّد فقط بأنّك تستدعيه في أعلى الملفّ عوضا عن أي مكان آخر. بعد الاستدعاء ستتمكّن من استخدامه كالمُعتاد. مثال على ماكرو هناك العديد من الاستخدامات لهذه الخاصيّة، وسنتعرّف على بعض منها أثناء تطويرنا للتّطبيق، وهذا ما جعلني أشرح الفكرة هنا لتُفهم أكثر. في هذا المثال سنقوم بإنشاء ماكرو لتحويل قائمة بايثون إلى قائمة HTML. فمثلا لو كانت لدينا القائمة التّالية: list = ['Abdelhadi', 'Ayman', 'Ibrahim'] فسنستطيع تحويلها إلى التّالي بسطر واحد: <ul> <li>Abdelhadi</li> <li>Ayman</li> <li>Ibrahim</li> </ul> أولا سنُنشئ الماكرو كما يلي: {% macro py_to_html(list) %} <ul> {% for item in list %} <li>{{ item }}</li> {% endfor %} </ul> {% endmacro %} ثمّ نستدعيه كما يلي (على فرض أنّ list عبارة عن قائمة بايثون عاديّة): {{ py_to_html(list) }} ما ستُلاحظه هو أنّ النّتيجة ستكون عبارة عن قائمة HTML تحتوي على عناصر القائمة list، وهذا طبيعي لأنّ ما يفعله الماكرو ببساطة هو الدّوران حول المُعامل list الذي يستقبله ويعرض كل عنصر من القائمة داخل وسمي <li></li>. وهذه هي ببساطة طريقة عمل الماكرو، ويُمكنك إعادة استعماله أكثر من مرّة مع تمرير مُعاملات أخرى دون الحاجة إلى إعادة الشّيفرة مرارا وتكرارا. يُمكنك كذلك إنشاء ماكرو بدون أيّة مُعاملات، فمثلا الماكرو التّالي سيقوم بعرض الجملة “مرحبا بالعالم” في كل مرّة يتمّ استدعاؤه فيه: {% macro hello_world() %} مرحبا بالعالم {% endmacro %} وطريقة استدعائه في قالب HTML ستكون كالتّالي: {{ hello_world() }} سنتعرّف أثناء تطويرنا لتطبيق “كلمة” على المزيد من الأمثلة لكيفيّة استعمال ميّزة الماكرو في مُحرّك القوالب Jinja، وإن تابعت سلسلة استعمال مكتبة WTForms مع إطار العمل Flask فلا شك بأنّك ستأخذ فكرة واضحة عن كيفيّة عمل الماكرو كما ستحصل على مثال واقعي لاستخدامه. المُرشّحات (filters) في Jinja2 المُرشّحات مبدأ جديد يتوجّب عليك فهمه لكتابة تطبيقات ويب أفضل. يُطبّق المُرشّح في مُحرّك القوالب Jinja على مُتغيّر لتغيير قيمته من حالة إلى أخرى. التّالي مثال على كيفيّة عرض قيمة المُتغيّر name في مُحرّك القوالب Jinja: {{ name }} إذا ما طبّقنا على هذا المتغيّر مُرشّحا فسيتوجّب علينا أن نقسم بين المُتغيّر والمُرشّح بعلامة |. المثال التّالي يُوضّح كيفيّة تطبيق مُرشّح وهمي سنُسمّيه filter على المُتغيّر name: {{ name | filter }} نستعمل المُرشّحات كما نستعمل في لغة Python دوال تأخذ قيمة وتقوم بعمليّة أو عدّة عمليّات على هذه القيمة وتُرجع القيمة النّهائيّة التّي تُمثّل النّتيجة. إذا ما طبقت مُرشّحا على مُتغيّر ما، فقيمة المُتغيّر يجب أن تتغيّر، وسنرى بعض أهم المُرشّحات التّي يُوفرّها مُحرّك القوالب Jinja فيما بعد. يُمكنك كذلك أن تُمرّر مُعاملات إلى مُرشّح عبر تحديد القيم داخل قوسين كما يلي: {{ name | filter(argument1, argument2, argument3) }} المُعاملات التّي تُمرّر تُحدّد نوعيّة العمليّات التّي ستخضع لها القيمة قبل أن تُرجع النّتيجة. يُمكن كذلك استخدام أكثر من مُرشّح عبر تقسيمها بالرمز | كما في المثال التّالي: {{ name | filter1() | filter2() | filter3() }} كل مُرشّح يجري عمليّة على القيمة التّي تسبقه، لذا فطريقة عمل المثال أعلاه هو كالتّالي: يأخذ المُرشّح filter1 قيمة المُتغيّر name ويُرجع نتيجة تُعتبر مُجرّد قيمة أخرى. تمرّ النّتيجة السّابقة على المُرشّح filter2 لنحصل على قيمة جديدة تُعتبر نتيجة لعمل المُرشّح filter2 على ما أرجعه المرشّح filter1. يأخذ المُرشّح filter3 القيمة التّي نحصل عليها في النّتيجة السّابقة ويُجري عليها عمليّة ثمّ يُرجع قيمة جديدة. وبما أنّ آخر قيمة حصلنا عليها هي ما أنتجه المُرشّح filter3 على ما سبقه من قيمة، فما سيظهر للمُستخدم الذي يزور صفحة HTML التّي حدث بها ما حدث هو القيمة الأخيرة. ما فائدة المُرشّحات؟ رغم أنّ المُرشّحات في Jinja يُمكن أن تُستبدل بعمليّات على المُتغيّرات قبل تقديم قالب HTML عند العمل مع إطار العمل Flask، إلّا أنّ استعمال المُرشّحات حل أفضل وأكثر مرونة، وفي بعض الحالات يُمكن أن تحفظ نفسك من تكرار الكثير من الشّيفرة إن استعملت مُرشّحا يُوفّره مُحرّك القوالب Jinja عوضا عن كتابة الشّيفرة باستعمال لغة Python في كل مرّة تحتاج فيها إلى تغيير قيمة إلى قيمة أخرى. بالإضافة إلى أنّك تستطيع إنشاء مُرشّحات خاصّة بك لتستعملها مع إطار العمل Flask وقوالب Jinja إن أردت ذلك، فمثلا يُمكنك أن تكتب مُرشّحا لتحويل تاريخ مُعيّن من الصّيغة العاديّة (1-1-2017) إلى صيغة (قبل كذا يوم/ قبل كذا شهر/ منذ X أشهر …). سنرى كذلك بعض الاستعمالات للمُرشّحات فيما بعد، فمثلا، سنرى كيفيّة استعمال مُرشّح تُوفّره لنا إضافة Flask-Gravatar (سنرى المزيد عن إضافات Flask فيما بعد) لتحويل بريد إلكتروني إلى الصّورة المُرتبطة به على خدمة Gravatar. مثال على استخدام مُرشّح لتحويل نصّ إلى أحرف كبيرة كمثال على كيفيّة استخدام مُرشّح مُتوفّر مُسبقا بحيث يُمكنك استخدامه في أي قالب HTML في مشروع Flask الخاصّ بك، سننظرُ إلى كيفيّة استخدام المُرشّح upper الذي يعمل بنفس طريقة عمل التّابع upper() في لغة بايثون، والذي يُمكنك استخدامه لتحويل نصّ عادي إلى أحرف كبيرة Uppercase. أولا، سنُضيف مُوجّها جديدا إلى ملفّ __init__.py داخل مُجلّد المشروع project، يُمكنك اعتبار هذا المُوجّه حقل تجارب لتختبر به مُختلف المُرشّحات التّي سأسردها فيما بعد ومن المُفضّل حذفه إذا انتهيت منه. سيكون المُوجّه كالتّالي: @app.route('/filters') def filters(): return render_template('filters.html') المُوجّه بسيط جدّا، والعمل الحقيقي سيكون داخل الملفّ filters.html الذي قدّمناه كجواب في الموجّه. أنشئ الملفّ filters.html داخل المُجلّد templates المُتواجد في مُجلّد المشروع project. داخل الملفّ filters.html، أضف ما يلي: {% extends 'base.html' %} {% block title %} كلمة – المُرشّحات {% endblock %} {% block content %} <h1> {{ "Hello World!" | upper() }} </h1> {% endblock %} لاحظ الجزء {{ "Hello World!" | upper() }}، إن تابعت ما سبق جيّدا، فستفهم بأنّنا قُمنا في هذا الجزء بتطبيق المُرشّح upper على السّلسلة النّصيّة Hello World ، وبالطّبع، فإنّك تستطيع أن تُعوّض السّلسلة النّصيّة بمُتغيّر يحمل نفس القيمة أو قيمة أخرى. إن زرت العنوان http://127.0.0.1:5000/filters فستُلاحظ النّص HELLO WORLD!، ما يعني بأنّ المُرشّح upper قد أدّى مهمّته. استعمال المُرشّحات مع نصّ مُتعدّد الأسطر في الأمثلة السّابقة، تعرّفنا على كيفيّة استعمال مُرشّح في سطر واحد كما يلي: {{ "Hello World!" | upper() }} لكن ماذا لو أردنا استعمال المُرشّح في جزء أكبر من الشّيفرة؟ كاستعمال المُرشّح upper مع فقرة كاملة أو نص متعدّد الفقرات. يُمكننا استخدام المُرشّح كما نستعمل الجملة الشّرطية if أو حلقة for في Jinja، وذلك عبر إحاطة ما نُريد تطبيق المُرشّح عليه بكل من {% filter upper %} و {% endfilter %}، مع تغيير upper بالمُرشّح الذي تُريد استعماله. المثال التّالي يُمثّل كيفيّة استعمال المُرشّح upper مع نصّ مُتعدّد الأسطر: {% filter upper() %} Hello World! {% endfilter %} خاتمة تعرّفنا في هذا الدّرس كيفيّة استعمال خاصيّة الماكرو في مُحرّك القوالب Jinja لتفادي تكرار شيفرة عدّة مرّات، كما تعرّفنا على ماهيّة مُرشّحات Jinja وكيفيّة استعمالها والهدف منها، في الدّرس التّالي، سنلقي نظرة على بعض من أهمّ المُرشّحات التّي تأتي مبنيّة مُسبقا في مُحرّك القوالب Jinja والتّي يُمكنك استعمالها مُباشرة مع تطبيقات فلاسك الخاصّة بك.
  21. مُقدّمة بعد أن تعرّفنا على كيفيّة استخدام المُرشّحات في قوالب HTML، سنتعرّف في هذه الفقرة على بعض من أهم المُرشّحات المُتوفّرة في مُحرّك القوالب Jinja، والتّي يُمكنك استعمالها مُباشرة مع مشاريع Flask الخاصّة بك. المُرشّح default لتوفير قيمة افتراضيّة في بعض الأحيان، يُمكن أن يكون متغيّرٌ غير مُتوفّرٍ في القالب، لكن يُمكنك توفير قيمة افتراضيّة لتظهر في هذه الحالة. كمثال على ذلك، لنفترض بأنّك تعرض عنوان مقال باستخدام السّطر التّالي: <h1> {{ title }} </h1> في حالة لم يكن المُتغيّر title مُعرّفا فقد يُشوّه ذلك مظهر الصّفحة أو من المُمكن أن تحدث أخطاء غير مُتوقّعة، يُمكننا عوضا عن ذلك عرض النّص Title Not Found باستخدام السّطر التّالي عوضا عن السّطر السّابق: <h1> {{ title | default("Title Not Found") }} </h1> يُمكننا الآن التّأكّد من أنّ الجملة Title Not Found تظهر فقط في حالة لم يكن المُتغيّر title مُعرّفا، وذلك عبر تعريفه قبل استدعائه باستخدام السّطر التّالي: {% set title = "A title for a post" %} تأكّد فقط من تعريف المُتغيّر في سطر يسبق السّطر الذي تستدعي فيه قيمته. عند تعريف المُتغيّر ستظهر قيمته بشكل عادي، أمّا إن لم يكن مُعرّفا فالجملة “Title Not Found” ستظهر عوضا عن ذلك. المُرشّح capitalize لتحويل الحرف الأول من كلمة إلى حرف كبير في بعض اللغات اللاتينيّة، من المُهمّ أن تجعل الحرف الأول من بعض الأسماء حرفا كبيرا، فمثلا كتابة اسم على شكل Ali طريقة تعبير أفضل من كتابته على شكل ali. ولتحويل كل قيمة مُعيّنة إلى هذه الحالة يُمكننا استعمال المُرشّح capitalize كما يلي: <h1> {{ "academy" | capitalize() }} </h1> نتيجة المثال السّابق ستكون الكلمة academy مكتوبة على شكل Academy، لاحظ بأنّ الحرف الأول أصبح كبيرا وبقيّة الأحرف عاديّة. المُرشّح title لتحويل قيمة نصيّة إلى طريقة كتابة العناوين لا شك أنّك لاحظت بأنّ مواقع الأخبار الانجليزيّة والمُدوّنات تكتب عناوين مقالاتها بحيث يكون الحرف الأول من كل كلمة حرفا كبيرا. فعوضا عن كتابة عنوان بالطّريقة المُواليّة: how to use the flask framework to develop web applications فإنّ الطّريقة الصّحيحة هي بكتابته كما يلي: How To Use The Flask Framework To Develop Web Applications ولحسن الحظ، فإنّ مُحرّك القوالب Jinja يُسهّل علينا مهمّة تحويل جميع العناوين المُتواجدة في قاعدة البيانات إلى الشّكل الصّحيح للعناوين دون الاضطرار إلى تعديلها واحدا واحدا. يكفي استخدام المُرشّح title لتحويل أي عنوان كيف ما كان إلى الشّكل الصّحيح، والتّالي مثال على كيفيّة استخدام هذا المُرشّح: <h1> {{ "how to use the flask framework to develop web applications" | title() }} </h1> بالإضافة إلى كل من upper، capitalize و title فالمُرشّح lower يقوم بتحويل أي سلسلة نصّيّة إلى أحرف صغيرة. المُرشّح first لعرض أول عنصر من مجموعة عناصر إذا كنت تتعامل مع مُتغيّر يحمل مجموعة من القيم كقائمة بايثون تحتوي على العديد من العناصر، فإنّك تستطيع عرض أول عنصر دون العناصر الأخرى عبر استخدام المُرشّح first. المثال التّالي يُوضّح كيفيّة استخدام المُرشّح first: {% set list = ["One", "Two", "Three"] %} <h1> {{ list | first() }} </h1> في المثال أعلاه، نستخدم الكلمة المفتاحيّة set لتعريف مُتغيّر باسم list والذي يحمل بدوره قائمة من ثلاثة قيم، أمّا في السّطر الذي يليه، فنُطبّق المُرشّح first على القائمة list التّي أنشأناها قبل قليل. إذا عدت إلى الصّفحة الخاصّة بالمُرشّحات الآن، فستُلاحظ بأنّ ما بداخل الوسم <h1> هو القيمة One فقط، وذلك لأنّها أول قيمة في مجموعة القيم المُتواجدة داخل القائمة list. المُرشّح float لتحويل الأعداد إلى أعداد عشريّة هذا المُرشّح يعمل بنفس طريقة عمل الدّالة float() في لغة بايثون، إذ يقوم بتحويل أي عدد مهما كان نوعه إلى عدد عشري. يُمكنك استخدامه كما يلي: {{ 10 | float() }} ستُلاحظ بأنّ العدد 10 قد حوّل ليُصبح على شكل 10.0. المُرشّح يعمل مع السّلاسل النّصيّة كذلك، لذا فالمثال التّالي سيقوم بإنتاج 10.0 كذلك: {{ "10" | float() }} إذا لم يكن بالمقدور تحويل القيمة إلى عدد عشري فستظهر القيمة 0.0 عوضا عن ذلك. يُمكنك تجربة القيمة الافتراضيّة بمُحاولة تحويل سلسلة نصيّة إلى عدد عشري بمثال مُشابه لما يلي: {{ "Hello Word" | float() }} ستُلاحظ بأنّ النّتيجة هي 0.0، وهي القيمة الافتراضية التّي تظهر إذا ما كانت القيمة غير قابلة للتّحويل إلى عدد عشري. يُمكنك تغيير القيمة الافتراضيّة 0.0 إلى أي قيمة أخرى عبر تمرير القيمة الافتراضيّة الجديدة إلى المُعامل default كما يلي: {{ "Hello Word" | float(default="Error: value cannot be converted into a floating point number") }} بعد هذا التّغيير، ستجد بأنّ نتيجة تحويل قيمة لا يُمكن تحويلها إلى عدد عشري هي الجملة “Error: value cannot be converted into a floating point number” ويُمكنك تغيير هذه الرّسالة كيفما تشاء. المُرشّح int لتحويل القيم إلى أعداد صحيحة يعمل هذا المٌرشّح بطريقة مُشابهة لكيفيّة عمل المُرشّحfloat، إذ أنّ المُرشّح int يُحوّل أي قيمة إلى عدد صحيح، ويُمكنك استخدامه كما يلي: {{ 10.0 | int() }} وكما الحال مع المُرشّحfloat، فإنّ المُرشّح int يُحوّل أي قيمة غير قابلة إلى التّحويل إلى عدد صحيح إلى القيمة 0، ويُمكنك تعديل هذه القيمة الافتراضيّة عبر تمرير القيمة الجديدة إلى المُعامل default كما يلي: {{ "Hello Word" | int(default="Error: value cannot be converted into an integer number") }} المُرشّح join لضمّ عناصر مجموعة من القيم وجمعها لتكون قيمة واحدة في لغة بايثون البرمجيّة، يُمكننا جمع عناصر قائمة أو مجموعة من السّلاسل النّصيّة لتصبح سلسلة نصيّة واحدة باستخدام التّابع join. في مُحرّك القوالب Jinja، يُمكننا استخدام المُرشّح join للوصول إلى نفس النّتيجة. يُمكنك استخدام المُرشّح join كما يلي: {{ [1, 2, 3] | join() }} ستُلاحظ بأنّ النّتيجة في المُتصفّح هي 123. إليك مثالا آخر: {{ ["One", "Two", "Three"] | join() }} هذه المرّة ستكون النّتيجة القيمة OneTwoThree. يُمكنك كذلك الفصل بين العناصر بفاصل عبر تمريره إلى المُرشّح كمُعامل. المثال الأول: {{ [1, 2, 3] | join('|') }} في هذا المثال، ستُلاحظ بأنّ النّتيجة هي 1|2|3 عوضا عن 123 لأنّنا وضعنا فاصلا بين عناصر القائمة. المثال الثّاني: {{ ["One", "Two", "Three"] | join("-") }} هذه المرّة ستُلاحظ بأنّ النّتيجة هي One-Two-Three. المُرشّح last المُرشّح last يعمل بطريقة مُعاكسة للمُرشّح first، إذ أنّ هذا الأخير يعرض أول قيمة من مجموعة قيم، والمُرشّح last يعرض آخر قيمة من المجموعة. المثال التّالي يُوضّح كيفيّة استعمال المُرشّح last لعرض آخر قيمة من القائمة names: {% set names = ['Kamal', 'Ali', 'Ahmed', 'Khaled'] %} <h1> {{ names | last() }} </h1> إذا طبّقت المثال أعلاه، فسيتوجّب أن تحصل على الاسم Khaled كنتيجة لأنّه آخر عنصر من القائمة names. المثال التّالي يجمع كلّا من المُرشّح first والمُرشّح last: {% set names = ['Kamal', 'Ali', 'Ahmed', 'Khaled'] %} <h1> First: {{ names | first() }} </h1> <h1> Last: {{ names | last() }} </h1> النّتيجة ستكون كالتّالي: First: Kamal Last: Khaled ختاما تعرّفنا في هذا الدّرس على جزء من أهم مُرشّحات Jinja التّي يُمكنك أن تعتمد عليها في تطوير تطبيقات فلاسك الخاصّة بك، وسنستعمل بعضا منها في تطوير تطبيق “كلمة” في ما يلي من الدّروس، مُرشّحات Jinja كثيرة ولا يُمكن أن نذكرها جميعها في درس واحد، لذا فسنُكمل تغطيّة بقيّة المُرشّحات المُهمّة في دروس مُقبلة لنمرّ بعدها إلى جانب آخر من تطوير الويب أثناء تطويرنا لتطبيق كبير في هذه السّلسلة، لذا ترقّب بقيّة الدّروس.
  22. مُقدّمة بعد أن تعرّفنا على جزء من أهمّ المُرشّحات التّي يُمكنك استعمالها لتفادي تكرار شيفرة عدّة مرّات والحصول على تجربة تطوير أحسن، سنُكمل في هذا الدّرس ما بدأناه بالتّعرفّ على قائمة مُرشّحات أخرى لتكون لديك فكرة أفضل عن كيفيّة استخدام مُحرّك القوالب Jinja والمُرشّحات المتواجدة به. المُرشّح length لقياس عدد عناصر مجموعة من القيم في لغة بايثون، يُمكنك استخدام الدّالة len لحساب عدد عناصر قائمة أو مجموعة من القيم. في مُحرّك القوالب Jinja، يُمكنك استخدام المُرشّح length للحصول على نفس النّتيجة. يُمكنك استخدام هذا المُرشّح كما يلي: {{ list | length }} مع استبدال المُتغيّر list بالمُتغيّر الذي يحمل القائمة التي ترغب بحساب عدد عناصرها. المثال التّالي عبارة عن توضيح لكيفيّة استعمال المُرشّح length للحصول على عدد عناصر القائمة comments لمنح المُستخدم فكرة عن عدد التّعليقات المُتوفّرة: {% set comments = ['Comment 1', 'Comment 2', 'Comment 3', 'Comment 4', 'Comment 5'] %} Comments({{ comments | length }}): {% for comment in comments %} <p> {{ comment }} </p> {% endfor %} إن جرّبت المثال في ملفّ filters.html فستُلاحظ نتيجة مُشابهة لما يلي: Comments(5): Comment 1 Comment 2 Comment 3 Comment 4 Comment 5 يُمكنك استخدام هذه الفكرة للإشارة إلى عدد التّعليقات في مقال مُعيّن (كما هو مُلاحظ في المثال أعلاه) أو الإشارة إلى عدد الرّسائل الجديدة أو عدد التّنبيهات أو أي مجموعة أخرى من القيم التّي سيُساهم الإشارة إلى عددها في تسهيل تجربة المُستخدم مع تطبيقك. مُلاحظة: يُمكنك كذلك استخدام المُرشّح count للحصول على نفس النّتيجة. المُرشّح list هذا المُرشّح يُحوّل قيمة ما إلى قائمة كما تفعل الدّالة list() في لغة بايثون. وإذا كانت القيمة عبارة عن سلسلة نصيّة فالنّتيجة ستكون عبارة عن قائمة من الحروف المتواجدة بالسّلسلة النّصيّة. المثال التّالي عبارة عن توضيح لكيفيّة تحويل الكلمة Hello إلى قائمة تحتوي على خمسة عناصر، كل عنصر منها يُمثّل حرفا من الكلمة Hello: {{ 'Hello' | list() }} ستكون النّتيجة كما يلي: ['H', 'e', 'l', 'l', 'o'] المُرشّح random المُرشّح random يختار عنصرا عشوائيّا من مجموعة قيم وتكون نتيجته مُختلفة في كل مرّة بدون ترتيب مُعيّن. يُمكنك استخدام هذا المُرشّح كما يلي: {{ ['One', 'Two', 'Three'] | random() }} إذا جرّبت المثال أعلاه في الملفّ filters.html وتوجّهت إلى العنوان http://127.0.0.1:5000/filters فستحصل على أحد عناصر القائمة أعلاه بشكل غير مُرتّب وغير مُتوقّع وكلّما أعدت تحميل الصّفحة ستتغيّر القيمة إلى قيمة أخرى وقد تحصل أحيانا على نفس القيمة أكثر من مرّة. يُمكنك استخدام هذا المُرشّح في تطبيقاتك لإضافة تفاعل أكثر إلى التّطبيق، فإن كان مثلا تطبيقك عبارة عن مُدوّنة فقد تعرض مقالا من المقالات بشكل عشوائي أو تعليقا من أحد المقالات أو غير ذلك. يُمكنك كذلك أن تعرض مقولة بشكل عشوائي في كل مرّة يُعاد فيها تحميل الصّفحة، والمثال التّالي يُوضّح هذه الفكرة: {% set quotes = ['Quote 1', 'Quote 2', 'Quote 3', 'Quote 4', 'Quote 5'] %} <div class="quote"> {{ quotes | random() }} </div> إذا ما قمت بتجريب هذا المثال فستحصل على أحد عناصر القائمة quotes في كلّ مرّة تُعيد فيها تحميل الصّفحة، و مع افتراض أنّ القائمة quotes تحتوي على مقولات شهيرة ومُميّزة فإنّ إضافة هذه الميّزة إلى مُدوّنتك أو تطبيق مُشابه سيُشكّل فرقا كبيرا. مُلاحظة: استعمال المُرشّح random يعني بأنّك ستقوم بجلب العديد من السّجلات من قاعدة البيانات لتُرجع سجّلا واحدا فقط على المُتصفّح للمُستخدم، ومُعظم قواعد البيانات من نوع SQL أو NoSQL تستطيع جلب سجل واحد فقط بشكل عشوائيّ، لذا فإنّ استعمال هذا المُرشّح في بعض الحالات غير مُجد، ومن المُفضّل استخدام قاعدة بياناتك للحصول على سجل بشكل عشوائيّ عوضا عن جلب جميع السّجلات وتطبيق المُرشّح random عليها. المُرشّح replace لتغيير قيمة إلى قيمة أخرى هذا مُرشّح آخر من المُرشّحات التّي استوحيت من لغة بايثون، إذ أنّ المُرشّح replace يعمل بنفس طريقة عمل التّابع replace في لغة بايثون، وذلك بأخذ قيمتين كمُعاملات، المُعامل الأول عبارة عن القيمة القديمة، والمُعامل الثّاني يُمثّل القيمة الجديدة. المثال التّالي يُوضّح كيفيّة عمل هذا المُرشّح: {{ 'Hello World' | replace('Hello', 'Hi') }} إذا قمت بتجربة المثال أعلاه فالنّتيجة ستكون Hi World بدل Hello World. يُمكنك كذلك استعمال المُرشّح replace مع مجموعة من القيم عوضا عن سلسلة نصيّة واحدة. المثال التّالي يُوضّح كيفيّة استخدام المُرشّح replace مع قائمة تحتوي على ثلاثة عناصر: {% set list = ['One', 'Two', 'Three'] %} {{ list | replace('One', 1) }} كما تُلاحظ، فقد استعملنا المُرشّح replace لاستبدال القيمة One بالقيمة 1، لذا فإنّ النّتيجة ستكون كالآتي: ['1', 'Two', 'Three'] المُرشّح reverse لعكس قيمة ما يُمكن أن تحتاج في بعض الأحيان إلى طريقة سريعة لعكس سلسلة نصّية. يُمكنك القيام بالأمر ببساطة باستعمال المُرشّح reverse كما في المثال التّالي: {{ "Hello World!" | reverse() }} ستُلاحظ بأنّ نتيجة المثال أعلاه هي كما يلي: !dlroW olleH أمّا بالنّسبة لمجموعات القيم مثل القوائم وغيرها فالأمر مُختلف قليلا، إذ أنّ تطبيق المُرشّح على قائمة سيُنتج كائنا من النّوع Iterator وهو نوع خاص يُمكنك استخدام حلقة for معه للوصول إلى كل عنصر على حدة وللوصول إلى جميع العناصر في آن واحد فسيتوجّب عليك تحويله إلى قائمة باستعمال المُرشّح list. لتتوضّح الصّورة، جرّب المثال التّالي في ملفّ filters.html: {% set list = ['One', 'Two', 'Three'] %} {{ list | reverse() }} ستُلاحظ نتيجة مُشابهة لما يلي: <list_reverseiterator object at 0x7fc0b6262518> لكن بعد استخدام المُرشّح list على النّتيجة كما هو مُوضّح في السّطر التّالي: {{ list | reverse() | list() }} فستحصل على النّتيجة التّاليّة: ['Three', 'Two', 'One'] وهي قائمة عناصرها مُرتّبة ترتيبا عكسيّا للقائمة list التّي عرّفناها من قبل: ['One', 'Two', 'Three'] مع التّأكيد على أنّك تستطيع الدّوران على نتيجة المُرشّح reverse بحلقة for دون الحاجة إلى تحويلها إلى قائمة بالمُرشّح list، بل من الأفضل الدّوران على نتيجة المُرشّح reverse مُباشرة لأنّ الدّوران عليها يتم بالوصول إلى كل عنصر على حدة عوضا عن تجميع كل عناصر قائمة ومن ثم الدّوران حولها. إليك مثالا على كيفيّة استخدام الحلقة for مُباشرة مع نتيجة المُرشّح reverse: {% set list = ['One', 'Two', 'Three'] %} <ul> {% for item in list | reverse() %} <li>{{ item }}</li> {% endfor %} </ul> النّتيجة: - Three - Two - One المُرشّح safe هذا المُرشّح مُهم ومُفيد جدّا، كما أنّ استعماله خطير كذلك، إذ يسمح لشيفرات HTML بالظّهور على أنّها آمنة، ما يعني بأنّها ستظهر في المُتصفّح كما كتبتها. لتوضيح الأمر، جرّب ما يلي: {{ "<h1>Hello World!</h1>" }} ستُلاحظ في المُتصفّح ما يلي بخط عادي: <h1>Hello World!</h1> هذا لأنّ مُحرّك القوالب Jinja لا يسمح بأن تُترجم شيفرات HTML مُباشرة إلى المُتصفّح لما في ذلك من أخطار أمنيّة. لكن إن كنت مُتأكّدا من أنّ شيفرة HTML المُتواجدة في مُتغيّر ما آمنة ولا تحتاج إلى إجراءات احتياطيّة، فيُمكنك استخدام المُرشّح safe لتُترجم الشّيفرة وتظهر على المُتصفّح بشكل عادي. لذا فالمثال التّالي سيُظهر الجملة Hello World! بخط كبير داخل الوسم <h1>: {{ "<h1>Hello World!</h1>" | safe }} المثال أعلاه قليل الورود لأنّ الهدف من المُرشّح safe هو إعلام مُحرّك القوالب Jinja بأنّ هذه القيمة عبارة عن شيفرة HTML آمنة، والقيمة عادة ما تكون داخل مُتغيّر أو على شكل عنصر من مجموعة عناصر. المثال التّالي يُوضّح كيفيّة استعمال المُرشّح safe لعرض قائمة تحتوي على شيفرات HTML: {% set list = ['<span div=list-item>One</span>', '<span div=list-item>Two</span>', '<span div=list-item>Three</span>'] %} <ul> {% for item in list %} <li>{{ item | safe }}</li> {% endfor %} </ul> كما تُلاحظ، فإنّ القائمة list تحتوي على عدّة عناصر، كل عنصر يحتوي على شيفرة HTML. لو قمنا بالدّوران حول القائمة وعرضنا كل عنصر دون استعمال المُرشّح safe، فستكون النّتيجة في المُتصفّح كما يلي: <span div=list-item>One</span> <span div=list-item>Two</span> <span div=list-item>Three</span> وهذه ليست النّتيجة التّي نُريدها، بل نُريد أن تُترجم الشّيفرة إلى لغة HTML يفهمها المُتصفّح. أمّا عند استعمال المُرشّح safe، فشيفرة HTML ستُترجم ليفهمها المُتصفّح وسيكون مصدر الصّفحة كما يلي: <ul> <li><span div=list-item>One</span></li> <li><span div=list-item>Two</span></li> <li><span div=list-item>Three</span></li> </ul> مُلاحظة: كن حذرا في استعمالك للمُرشّح safe وتأكّد أولا من أنّ شيفرة HTML التّي تُريد عرضها في المُتصفّح آمنة بشكل تام، فإن حدث وأن أسأت استعمال هذا المُرشّح فقد تفتح مجالا لهجمات XSS ما قد يُخرّب تطبيقك أو يسمح للمُهاجم بالوصول إلى قاعدة بيانات التّطبيق أو غير ذلك من الأعمال الخبيثة. خاتمة تعرّفنا في هذا الدّرس على المزيد من المُرشّحات التّي يُوفرّها مُحرّك القوالب Jinja لتتمكّن من تطوير قوالب HTML بشكل أسرع وبأقل تكرار ممكن للشّيفرة. لكنّنا لم ننته بعد، إذ لا تزال في جعبة مُحرّك القوالب Jinja المزيد من المُرشّحات المُفيدة والتّي سنتعرّف عليها في الدّرس المُقبل.
  23. مُقدّمة تعرّفنا في الدّرس السّابق على بعض من مُرشّحات مُحرّك القوالب Jinja، مثل المُرشّح length لحساب عدد عناصر مجموعة ما، والمُرشّح list لتحويل القيم إلى قائمة، والمُرشّح replace لتعويض قيمة من نص ما بقيمة أخرى، وكذلك المُرشّح reverse لعكس قيمة نصيّة أو مجموعة من القيم، وانتهينا بالتّعرف على المُرشّح safe لإعلام Jinja بأنّ القيمة عبارة عن شيفرة HTML آمنة، في هذا الدّرس سنُكمل ما بدأناه بالتّعرف على المزيد من المُرشّحات المهمّة لننتقل بعد ذلك إلى كيفيّة إنشاء مُرشّح خاص بك مع مجموعة من الأمثلة في درس مُقبل. المُرشّح sort لترتيب قيمة قابلة للدّوران عليها يُمكنك استخدام المُرشّح sort لترتيب أي قيمة من النّوع Iterable، بمعنى آخر، أي قيمة يُمكنك الدّوران عليها والوصول إلى كل عنصر من عناصرها باستعمال حلقة for، وتشمل هذه القيم القوائم والمجموعات والسّلاسل النّصيّة وقيما أخرى. التّرتيب يكون من الأصغر إلى الأكبر بالنّسبة للأعداد، وبشكل أبجدي عند التّعامل مع السّلاسل النّصيّة. المثال التّالي يُوضّح كيفيّة ترتيب الأعداد من أصغر عدد إلى أكبر عدد: {{ "3241" | sort}} ستُلاحظ بأنّ النتيجة عبارة عن قائمة كما يلي: ['1', '2', '3', '4'] والمثال التّالي يوضّح كيفيّة استعمال المُرشّح مع سلسلة نصيّة لترتيبها أبجديّا: {{ "adbc" | sort}} النّتيجة ستكون عبارة عن قائمة أيضا: ['a', 'b', 'c', 'd'] وبما أنّ قائمة عاديّة عبارة عن Iterable كذلك، فيُمكنك استخدام المُرشّح sort مع القوائم بنفس الطّريقة: {{ ['c', 'b', 'd', 'a'] | sort() }} النّتيجة: ['a', 'b', 'c', 'd'] يُمكنك كذلك ترتيب كلمات كاملة: {{ ['Ali', 'Do', 'Flask', 'Ball'] | sort() }} النّتيجة: ['Ali', 'Ball', 'Do', 'Flask'] وعند جمع السّلاسل النّصيّة مع الأعداد فالنّتيجة ستبدأ أولا بالأعداد ثمّ السّلاسل النّصيّة بشكل مُرتّب ترتيبا أبجديّا: {{ '41532dabec' | sort() }} النّتيجة: ['1', '2', '3', '4', '5', 'a', 'b', 'c', 'd', 'e'] بعد بضعة تجارب، لاحظت بأنّ التّرتيب ممكن كذلك مع الكلمات العربيّة، ويُمكنك تجربة المثال التّالي لتتأكّد: {{ ['أب', 'جبل', 'ثلاثة', 'ترجم'] | sort() }} النّتيجة ستكون كما يلي: ['أب', 'ترجم', 'ثلاثة', 'جبل'] وبالطّبع، فإنّ أنسب استعمال لهذا المُرشّح يكون مع حلقة for لأنّ النّتيجة دائما ما تكون قائمة عاديّة، لذا سيتوجّب عليك الدّوران حولها لعرض كل عنصر على حدة. المُرشّح sum للحصول على جمع أعداد من مجموعة من القيم في بعض الأحيان، قد ترغب بجمع أعداد تتواجد بقائمة أو مجموعة عناصر عبارة عن أعداد مثل أسعار السّلع في قائمة أو عدد نقاط المُستخدم أو غير ذلك من الأعداد التّي سيُفيدك جمعها للحصول على قيمة إجماليّة. لنضرب مثالا لقائمة مُشتريات، كل عنصر يُمثّل سعر بضاعة ما، وسيتوجّب عليك عرض القيمة الإجماليّة لقائمة المُشتريات وذلك بجمع الأسعار. لنضع القائمة التّالية كمثال لأسعار ثلاثة سلع: [12, 14, 2] للوصول إلى السّعر الإجمالي، سيتوجّب عليك جمع كل من 12 مع 14 ثمّ إضافة 2 للنّتيجة. وهذا بالضّبط ما يقوم به المُرشّح sum. إليك المثال التّالي: {% set prices = [12, 14, 2] %} {{ prices | sum() }} النّتيجة ستكون العدد 28 وهي نتيجة العمليّة الحسابيّة 12 + 14 + 2. يُمكنك كذلك جمع الأعداد إن كانت مُتواجدة في خاصيّة، وذلك لأنّ سعر سلعة أو بضاعة مُعيّنة قد يكون داخل خاصيّة مثل price أو اسم آخر، لذا فللوصول إلى سعر سلعة ما سيتوجّب عليك القيام بذلك كما يلي: item.price لذا فطريقة استعمال المُرشّح sum في هذه الحالة مُختلفة قليلا، إذ سيتوجّب عليك تمرير اسم الخاصيّة إلى المُعامل attribute كما يلي: {{ items | sum(attribute='price') }} المثال التّالي سيقوم بالدّوران حول عناصر المتغيّر items والوصول إلى سعر كل عنصر عبر الخاصيّة price ومن ثمّ جمعها للحصول على السّعر الإجمالي. المُرشّح truncate لعرض جزء صغير من نص طويل إذا تصفّحت بعض المُدوّنات ومواقع الدّروس المكتوبة فستُلاحظ بأنّ الصّفحة التّي تعرض قائمة المقالات والدّروس تتميّز بكونها مُتناسقة بحيث تعرض جزءا صغيرا فقط من المقال الكامل لتتمكّن من الوصول إلى صفحته وقراءته كاملا عبر زر "اقرأ المزيد” أو ما شابه ذلك. يُمكننا أن نقوم بهذه المُهمّة ببساطة باستعمال المُرشّح truncate، كما يُمكنك التّحكم بحجم النّص المُختصر بكل سهولة. المثال التّالي يُوضّح كيفيّة استخدام المُرشّح مع نص طويل: {{ "مقال طويل يحتاج إلى تقصيره وعرض جزء صغير منه فقط ليكون تنسيق الصّفحة التّي تعرض عناوين المقالات أحسن" | truncate(length=50) }} النّتيجة: مقال طويل يحتاج إلى تقصيره وعرض جزء صغير منه ... يُمكنك تغيير طول النّص النّاتج بتغيير قيمة المُعامل length: {{ "مقال طويل يحتاج إلى تقصيره وعرض جزء صغير منه فقط ليكون تنسيق الصّفحة التّي تعرض عناوين المقالات أحسن" | truncate(length=25) }} هنا غيّرنا قيمة المُعامل length وجعلناها 25 عوضا عن 50، لذا فالنّتيجة ستكون هذه المرّة كما يلي: مقال طويل يحتاج إلى ... المُرشّح urlize لتحويل الروابط النّصيّة إلى روابط قابلة للضّغط عليها المُرشّح urlize مُرشّح مُفيد جدّا، إذ يقوم بتحويل أي رابط نصيّ إلى رابط يُمكنك الضّغط عليه للوصول إلى الموقع المُرتبط بذلك الرّابط. لتفهم أكثر، جرّب المثال التّالي: {{ "https://academy.hsoub.com/" }} النّتيجة مُتوقّعة، السّلسلة النّصيّة أعلاه ستظهر بشكل نص عادي ولا يُمكنك الضّغط عليها للوصول إلى موقع الأكاديميّة. الآن، حاول استعمال المُرشّح urlize مع النّص السّابق: {{ "https://academy.hsoub.com/" | urlize() }} ستُلاحظ بأنّ الرّابط في المُتصفّح أصبح مُختلفا وستتمكّن الآن من الضّغط عليه للوصول إلى موقع الأكاديميّة، وإن اطّلعت على مصدر الصّفحة فستُلاحظ ما يلي: <a href="https://academy.hsoub.com/">https://academy.hsoub.com/</a> ويُمكنك كذلك تجاهل الجزء http:// أو https:// ووضع رابط عادي كما يلي: {{ "academy.hsoub.com" | urlize() }} رغم ذلك الرّابط سيكون قابلا للضّغط عليه وسيوصلك إلى موقع الأكاديميّة. يُمكنك كذلك تقصير طول الرّابط الذي يظهر في المُتصفّح إلى طول تستطيع التّحكم به، وذلك بتمرير الطّول إلى المُرشّح كمُعامل كما يلي: {{ "https://academy.hsoub.com/tags/flask%20101/" | urlize(21) }} النّتيجة ستكون كما يلي: https://academy.hsoub... وعند النّقر على الرّابط ستتوجّه إلى صفحة سلسلة دروس Flask للمُبتدئين. المُميّز في هذا المُرشّح أنّه يُحوّل الرّوابط لتُصبح قابلة للضّغط عليها حتى ولو كانت مُدمجة داخل نص طويل يحتوي على كلمات أخرى لا علاقة لها بالرّابط، أي أنّه يُحوّل فقط الرّوابط داخل النّص وليس النّص بأكمله. إليك المثال التّالي لتتوضّح الفكرة: {{ " اضغط على الرّابط التّالي للوصول إلى سلسلة دروس إطار العمل فلاسك للمُبتدئين: https://academy.hsoub.com/tags/flask%20101/ أو تابع دروس بايثون عبر النّقر على الرّابط التّالي: https://academy.hsoub.com/programming/python/ " | urlize() }} ستُلاحظ بأنّ كلّا من الرّابطين يعملان بشكل صحيح وأنّ النّصوص الأخرى عاديّة. يُمكنك كذلك تقصير الرّوابط عبر تمرير طول إلى المُرشّح وسيُطبَّق على جميع الرّوابط، فمثلا نتيجة تمرير العدد 30 كمُعامل للمُرشّح urlize إلى المثال أعلاه هي كالتّالي: اضغط على الرّابط التّالي للوصول إلى سلسلة دروس إطار العمل فلاسك للمُبتدئين: https://academy.hsoub.com/tags... أو تابع دروس بايثون عبر النّقر على الرّابط التّالي: https://academy.hsoub.com/prog... المُرشّح wordcount لحساب عدد كلمات نص معيّن هذا المُرشّح لا يحتاج إلى الكثير من الشّرح، إذ أنّ فكرته بسيطة، إذا ما طبّقته على نص مُعيّن فستحصل على عدد الكلمات في النّص. مثال: {{ 'واحد اثنان ثلاثة' | wordcount() }} النّتيجة ستكون 3 لأنّ النّص أعلاه يحتوي على ثلاثة كلمات. المُرشّح wordwrap لنسيق النّصوص الطّويلة. المُرشّح wordwrap يقوم بتنسيق النّصوص الطّويلة بتقسيمها على عدّة أسطر، كل سطر لا يتعدّى 79 حرفا (يُمكنك تغيير هذا الطّول). إليك مثالا على كيفيّة استخدام المُرشّح مع نص طويل نفترض أنّه مُتواجد داخل المُتغيّر text: {{ text | wordwrap() }} لكن رغم تطبيق المُرشّح على النّص فالنّتيجة لن تظهر إلّا عند تصفّحك لمصدر الصّفحة، وذلك لأنّ المُرشّح لا يُقسّم الأسطر باستعمال الوسم <br> افتراضيّا، بل يجب عليك أن تقوم بتحديد نوع التّقسيم الذي تُريده أولا عبر تمرير الفاصل إلى المُعامل wrapstring. لذا للفصل بين الأسطر باستعمال الوسم <br> سيتوجّب علينا أن نُمرّره أولا إلى المُعامل wrapstring ثمّ تطبيق المُرشّح safe لإعلام Jinja بأنّ شيفرة HTML بداخل النّص آمنة (مُجدّدا، تأكّد أولا من أنّ النّص آمن بالفعل قبل تطبيق هذا المُرشّح). لذا فالمثال أعلاه سيكون كما يلي: {{ text | wordwrap(wrapstring='<br>') | safe() }} وكما قلت سابقا، فالمُرشّح يقوم بالفصل بين سطر وسطر آخر حسب طول مُحدّد، والقيمة الافتراضيّة لهذا الطّول هو 79 ويُمكنك تغييره إلى ما تشاء عبر تمرير القيمة التّي تُريدها إلى المُعامل width كما يلي: {{ text | wordwrap(width=20, wrapstring='<br>') | safe() }} المُرشّح select لعرض نتيجة حسب اختبار ما الاختبارات Tests في مُحرّك القوالب Jinja طريقة أخرى لمُعالجة القيم، وسنتطرّق إليها بشكل مُفصّل لاحقا، ما يجب أن تعرفه الآن هو أنّ المُرشّح select يُمكّنك من تطبيق اختبار على مجموعة من القيم لتُعرَض العناصر التّي تجتاز الاختبار وتُتجاهل العناصر الأخرى. سنُلقي نظرة على عدّة أمثلة لتفهم الأمر. أولا، سنُطبّق الاختبار odd على قائمة أعداد لاختيار الأعداد الفرديّة منها. المثال الأول: {% set numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] %} {{ numbers | select("odd") | list() }} لاحظ بأنّنا استعملنا المُرشّح list على النّتيجة لتحويلها إلى قائمة وذلك لأنّها في الأصل كائن من النّوع Generator وهو نوع شبيه بالنّوع Iterator، أي أنّك تستطيع استخدام حلقة for عليه للوصول إلى كل عنصر على حدة، لكن لرؤية العناصر كاملة فسيتوجّب عليك تحويل النّتيجة إلى قائمة. النّتيجة: [1, 3, 5, 7, 9] لابدّ أنّك لاحظت بأنّ النّتيجة لا تحتوي سوى على الأعداد الفرديّة من القائمة numbers، وهذا لأنّنا طبّقنا عليها المُرشّح select والاختبار odd (الذي يختبر ما إذا كانت قيمة عددا فرديّا أم لا). المثال الثّاني: في المثال الثّاني سنستعمل الاختبار string الذي يتحقّق ما إذا كانت القيمة سلسلة نصيّة أو لا. في هذا المثال، سنُطبّق المُرشّح select مع الاختبار string على قائمة تحتوي على خليط من الأعداد والسّلاسل النّصيّة: {% set numbers = [1, 'Flask', 3, 42, 'Python', 'Hello World!', 2312, 2017, 'Jinja'] %} {{ numbers | select("string") | list() }} النّتيجة: ['Flask', 'Python', 'Hello World!', 'Jinja'] ستُلاحظ بأنّ الاختبار تجاهل جميع الأعداد لأنّها ليست سلاسل نصيّة، وفي المُقابل النّتيجة تحتوي على السّلاسل النّصيّة فقط. خاتمة تعرّفنا في الدّروس السّابقة وهذا الدّرس على كيفيّة التّعامل مع مُحرّك القوالب Jinja الذي يكون مُرفقا مع إطار العمل فلاسك، خاصّة كيفيّة العمل مع خاصيّة المُرشّحات والاستفادة منها، هذا الدّرس هو آخر درس يتحدّث عن المُرشّحات المبنيّة مُسبقا في Jinja ما يعني بأنّنا سنضطر إلى إنشاء مُرشّحات خاصّة بنا لتأدية أغراض أخرى غير التّي بُنيت لها المُرشّحات المبنيّة مُسبقا، لذا فالدّرس القادم سيكون عبارة عن مدخل إلى كيفيّة إنشاء مُرشّحات خاصّة بك مع مُساعدة من إطار العمل Flask للحصول على تجربة تطوير أفضل وتفادي تكرار شيفرات HTML.
  24. مُقدّمة تعرّفنا في الدّروس السّابقة على كيفيّة الاستفادة من مُحرّك القوالب Jinja أثناء تطوير تطبيقات ويب باستخدام إطار العمل Flask ، خاصّة ميّزتي الماكرو والمُرشّحات، اللّتان تُعتبران من أهمّ ميّزات Jinja، وبعد أن ألقينا نظرة على أهم المُرشّحات المبنيّة مُسبقا، يُمكن ألّا تجد ما يسدّ حاجتك في هذه المُرشّحات وقرّرت أنّك تملك أفكارا أخرى يُمكن أن تحوّلها إلى مُرشّح لمُساعدتك على تفادي تكرار الشّيفرة والتطوير بشكل أسرع، إن كان الأمر كذلك، فهذا الدّرس هو كل ما تحتاج إليه لتعلّم كيفيّة إنشاء مُرشّحات أخرى وكيفيّة استعمالها مع إطار العمل Flask، إذ سنستخدم مشروع “كلمة” الذي نحن بصدد بنائه كمثال على كيفيّة إنشاء واستخدام مُرشّحاتك الخاصّة. إنشاء مُرشّحات خاصّة بك بعد أن تعرّفنا على كيفيّة استعمال المُرشّحات المبنيّة مُسبقا في 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> نتيجة المثال أعلاه ستكون مُشابهة لما يلي: خاتمة تعرّفنا في هذا الدّرس والدّروس السّابقة على كيفيّة استخدام مُرشّحات Jinja وكيفيّة إنشاء مُرشّحات خاصّة بك، لذا ستتمكّن الآن من استعمال ما تعلّمته لتطوير تطبيقات أكثر تعقيدا لتُلبي احتياجاتك وتُطبّق أفكارك بسهولة وطرق أحسن من ذي قبل. في الدّرس المُقبل، سنُنهي سلسلة الدّروس المُخصّصة لمُحرّك القوالب Jinja عبر التّعرف على مبدأ الاختبارات ومن ثمّ ننتقل إلى إكمال تطوير تطبيق "كلمة” في بقيّة الدّروس.
  25. flask_cms

    مُقدّمة بعد أن تعرّفنا على ماهيّة المُرشّحات وكيفيّة استخدام أهم المُرشّحات المبنيّة مُسبقا في Jinja وبعد أن تعرّفنا على كيفيّة إنشاء مُرشّحات خاصّة بنا، سننتقل إلى ميّزة أخرى لا تقلّ أهميّة عن المُرشّحات، ألا وهي ميزة الاختبارات Tests في مُحرّك القوالب Jinja، إذ سنتعرّف أولا على ماهيّة الاختبارات والغرض من استعمالها وكيفيّة الاستفادة منها في تطبيقات Flask الخاصّة بك ثمّ سننتقل إلى التّعرف على بعض من أهم الاختبارات التّي يُمكنك الاعتماد عليها لتطوير تطبيقاتك. ما معنى اختبارات Jinja في ما سبق من الدّروس، تعرّفنا على حقيقة أنّ مُرشّحات Jinja تقوم بتحويل قيمة من حالة إلى أخرى، في اختبارات Jinja لا تتحوّل القيمة، بل تُختبر ضدّ اختبار (Test) للتأكد من أنّها تندرج ضمن حالة مُعيّنة، ومنه تستطيع تطبيق اختبار على كائن للتّحكم بما يُعرض في المُتصفّح وما لا يُعرض حسب ما إذا اجتاز اختبارا أو لم يجتزه. طريقة إجراء الاختبارات هي باستعمال الكلمة المفتاحيّة is عوضا عن الرّمز | الذي يُستعمل لتطبيق مُرشّح مُعيّن. على سبيل المثال، يُمكنك التّحقق ممّا إذا كان مُتغيّر مُعرّفا أو لا كما يلي: name is defined في المثال أعلاه اسم المُتغيّر الذي يُجرى عليه الاختبار هو name والاختبار هو defined، وهو اختبار مُتوفّر مُسبقا في مُحرّك القوالب Jinja، وكما فعلنا مع المُرشّحات المبنيّة مُسبقا، فسنتعرّف على أهم الاختبارات التّي يُمكنك الاستفادة منها مُباشرة مع Jinja. يُمكن كذلك للاختبارات أن تقبل مُعاملات لمرونة أكثر في الاستخدام، فمثلا يُمكنك استخدام الاختبار divisibleby للتّحقق من أنّ عددا مُعيّنا يقبل القسمة على عدد آخر، العدد الأول هو القيمة التّي تُجرى عليها الاختبار، أمّا العدد الثّاني فهو القيمة المُمرّرة إلى الاختبار كمُعامل. مثال: 9 is divisibleby(3) إذا كان الاختبار يقبل مُعاملا واحدا فقط، فيُمكنك تمرير المُعامل دون أقواس، لذا فالمثال التّالي يقوم بنفس الشّيء الذي يقوم به المثال أعلاه: 9 is divisibleby 3 نتيجة الاختبار ستكون إمّا القيمة المنطقيّة True أو القيمة المنطقيّة False، ما يعني بأنّك تستطيع الاستفادة من نتيجة الاختبار باستعمالها مع الجمل الشّرطية if و else و elif. لتفهم هذا الدّرس أكثر، يُمكنك استعمال ملفّ filters.html الذي أنشأناه سابقا لتطبيق الأمثلة وتجربة أفكارك إذا ما شئت، إذ يُمكنك تفعيل البيئة الوهميّة وتعريف مُتغيّرات البيئة ومن ثمّ تشغيل الخادوم المحلي لتطبيق “كلمة” عبر كتابة الأمر flask run في سطر الأوامر، وبعدها كتابة المثال التّالي في ملفّ filters.html وزيارة العنوان http://127.0.0.1:5000/filters لتُلاحظ نتيجة تطبيق الاختبار divisibleby مع أعداد مُختلفة. المثال: {{ 10 is divisibleby 2 }} <br> {{ 4 is divisibleby 3 }} <br> {{ 9 is divisibleby 2 }} <br> {{ 60 is divisibleby 6 }} النّتيجة: True False False True يُمكنك تغيير الأعداد كما تشاء، والنّتيجة ستختلف حسب ما إذا كان العدد الأول قابلا للقسمة على العدد الآخر أم لا. ما الغرض من استعمال الاختبارات؟ قد لا تجد فائدة كثيرة في الاختبارات بعد أن تعرّفت على ماهيّتها، لكن الأمر أبعد من اختبار ما إذا كانت القيمة تقبل القسمة على عدد أم لا، إذ تستطيع استعمال الاختبارات لتحديد ما إذا كان كائن قابلا للاستدعاء أم لا، أو ما إذا كان يُساوي قيمة مُعيّنة أو التّأكّد من أنّ شيفرة مُعيّنة قد مُنعت من العرض على المُتصفّح كما هي لتفادي الهجمات الخبيثة، بالإضافة إلى الكثير من الأشياء الأخرى التّي يُمكنك أن تستعمل الاختبارات لأجلها، ما سيُتيح لك تحكّما أكثر بما يُعرض على مُتصفّح مُستخدم تطبيقك وما لا يُعرض. وبالإضافة إلى ما سبق، فستتعرّف على أهميّة الاختبارات حالما نُلقي نظرة على بعض من أهم الاختبارات التّي يُوفرّها مُحرّك القوالب Jinja وكيفيّة الاستفادة منها، ما سيُساعدك على معرفة الغرض من هذه الخاصيّة وأهمّيتها. قائمة ببعض من أهم اختبارات مُحرّك القوالب Jinja الآن وقد تعرّفنا على ماهيّة الاختبارات في مُحرّك القوالب Jinja، حان الوقت للتّعرف على أهمّ الاختبارات المبنيّة مُسبقا في Jinja والتّي يُمكنك استخدامها مُباشرة. الاختبار defined بالإضافة للمُرشّح default الذي تعرّفنا عليه مُسبقا، والذي يُمكّننا من توفير قيمة افتراضيّة لمُتغيّر مُعيّن في حالة لم يكن مُعرّفا، فالاختبار defined يُمكّنك من التّعرف على ما إذا كان مُتغيّر مُعيّن مُعرّفا أو لا، ما سيُمكّنك من اتّخاذ الإجراءات اللازمة في حالة لم يكن مُعرّفا. يُمكنك استخدام المثال التّالي لتفهم أكثر الغرض من الاختبار defined. {% if variable is defined %} قيمة المُتغيّر: {{ variable }} {% else %} المُتغيّر غير مُعرّف {% endif %} المثال أعلاه يتحقّق ممّا إذا كان المُتغيّر variable مُعرّفا أو لا، إذا كان مُعرّفا فالنّتيجة ستكون الجملة “قيمة المُتغيّر: ” بالإضافة إلى قيمة المُتغيّر، أمّا إذا لم يكن المُتغيّر مُعرّفا فالنّتيجة ستكون الجملة “المُتغيّر غير مُعرّف”. إذا قُمت بتجربة المثال في ملفّ filters.html فستحصل على النّتيجة الثّانيّة، ولتغيير الوضع يُمكنك تعريف المُتغيّر فوق جملة if الشّرطية كما يلي: {% set variable = 'مرحبا بالعالم!' %} بعد تعريف المُتغيّر ستجد بأنّ النّتيجة أصبحت الجملة “قيمة المُتغيّر: مرحبا بالعالم! ” عوضا عمّا سبق. الاختبار callable باستعمال الاختبار callable يُمكنك التّحقق ممّا إذا كان كائن أو مُتغيّر ما قابلا للاستدعاء أو لا، الكائنات القابلة للاستدعاء هي الدّوال، الماكرو وأصناف لغة بايثون وتوابع الأصناف وبعض الأنواع الأخرى. المثال التّالي سيُساعدك على فهم الأمر: {% macro say_hello() %} مرحبا بالعالم! {% endmacro %} {% if say_hello is callable %} الكائن قابل للاستدعاء ونتيجة استدعائه هي: <br> {{ say_hello() }} {% else %} لا يُمكن استدعاء الكائن {% endif %} في المثال، أنشأنا ماكرو بسيطا يقوم بإرجاع الجملة “مرحبا بالعالم!” بعدها طبّقنا الاختبار callable على الماكرو، والنّتيجة هي كما يلي: الكائن قابل للاستدعاء ونتيجة استدعائه هي: مرحبا بالعالم! ما يعني بأنّ الماكرو قابل للاستدعاء، ولو لم يكن كذلك لكانت النّتيجة هي الجملة “لا يُمكن استدعاء الكائن”. يُمكنك اختبار الحالة الأخرى (عدم قابليّة استدعاء الكائن) عبر حذف الماكرو وتعريف المُتغيّر say_hello مكان الماكرو باستعمال السّطر التّالي: {% set say_hello = 'مرحبا بالعالم!' %} وبما أنّ مُتغيّرا عاديّا لا يُمكن استدعاؤه، فالنّتيجة ستكون الجملة “لا يُمكن استدعاء الكائن ”، وذلك لأنّ الاختبار callable يُرجع القيمة المنطقيّة Flase عند استعماله مع متغيّرات بايثون عاديّة أو خصائص الأصناف لأنّها غير قابلة للاستدعاء. الاختباران even و odd إذا أردت التّحقق ممّا إذا كان عدد ما زوجيا أو لا فيُمكنك استعمال الاختبار even للقيام بالأمر، إذ يُرجع الاختبار إذا ما طُبق على عدد القيمة المنطقيّة True إذا كان العدد زوجيّا، أمّا إن لم يكن كذلك فالنّتيجة ستكون القيمة False. أمّا الاختبار odd فيقوم بالعكس، إذ يُستعمل للتّحقق من أنّ عددا ما فردي أو لا. المثال التّالي توضيح لكيفيّة استخدام كل من even و odd: {% set numbers = [1, 2, 3, 4, 5, 6] %} {% for number in numbers %} {% if number is even %} العدد {{ number }} عدد زوجي <br> {% elif number is odd %} العدد {{ number }} عدد فردي <br> {% endif %} {% endfor %} في المثال أعلاه، نُعرّف أولا قائمة أعداد ثمّ ندور حولها باستخدام حلقة for، في كل دورة نتحقّق ممّا إذا كان العدد زوجيّا لعرض رسالة تُفيد بأنّ العدد زوجي، وإن لم يكن العدد زوجيّا نتحقّق مّما إذا كان عددا فرديا لنعرض رسالة مضمونها أنّ العدد فردي. النّتيجة: العدد 1 عدد فردي العدد 2 عدد زوجي العدد 3 عدد فردي العدد 4 عدد زوجي العدد 5 عدد فردي العدد 6 عدد زوجي ختاما تعرّفنا في هذا الدّرس على ماهيّة ميّزة الاختبارات في مُحرّك القوالب Jinja، وتعرّفنا على الغرض منها وكيفيّة استعمالها وعلى بعض الاختبارات المبنيّة مُسبقا، في الدّرس القادم، سنُكمل ما بدأناه بالتّعرف على المزيد من الاختبارات المتواجدة بمُحرّك القوالب Jinja والتّي ستُساعدك على تطوير تطبيقات ويب أفضل كما ستُفيدك في فهم وتطوير تطبيق “كلمة”.