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

استخدام أطر العمل في برمجة تطبيقات الويب: فلاسك نموذجا


أسامة دمراني

سنغطي في هذا المقال من سلسلة تعلم البرمجة ما يلي:

  • أساسيات أطر عمل الويب.
  • استخدام إطار Flask في مثال Hello World.
  • بناء برنامج دليل جهات الاتصال في تطبيق ويب.

أطر عمل الويب Web Frameworks

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

  • صفحات خوادم مايكروسوفت النشطة Active Server Pages: واختصارًا ASP، يعمل هذا الإطار جنبًا إلى جنب مع خادم ويب مايكروسوفت IIS وبيئة "‎.NET" الخاصة بها، وهذا يعني إمكانية استخدام أي لغة مبنية على "‎".NET -بما في ذلك نسخة بايثون الخاصة بها- في إطار ASP.
  • صفحات خوادم جافا Java Server Pages: واختصارًا JSP، هي إطار عمل جافا الخاص بتطوير الويب، و هي تقنية لتوليد تطبيقات الخوادم المصغرة Servlets، التي هي عمليات خفيفة تشبه برامج CGI، لكنها أسهل في التعامل معها.
  • إطار عمل Ruby on Rails: يشبه إطار العمل ريلز rails الخاص بلغة روبي إطار عمل Flask الخاص ببايثون، والذي سندرسه قريبًا.
  • إطار عمل جانغو Django: يُعد جانغو أكثر أطر عمل بايثون شهرةً، خاصةً في بناء المواقع الكبيرة، وله شروحات كثيرة في كتب وتوثيقات إلكترونية، وهو غني بوحدات الإضافات plug-in modules المتاحة لتوسيعه، إلا أنه صعب التعلم موازنةً بإطار Flask.
  • إطار العمل Flask: وهو ما سندرسه في هذا المقال، إذ يوفر جميع العناصر الأساسية الموجودة في أطر العمل، وهو سهل التعلم نسبيًا، ويمكن استخدامه ببساطة دون كثير من التعقيد الذي يشتت الانتباه عن الأفكار الرئيسية.

تتمثل الفكرة الأساسية في جميع أطر عمل الويب في أنها تقلل الحمل على الخادم، باستخدام تقنيات البرمجة المتزامنة، والتي سندرسها في المقال التالي، كما تمقال المنطق logic عن شيفرة العرض presentation code، لتسهيل صيانة الموقع، حيث تسعى أحدث المعايير الصادرة عن w3c في كل من HTML5 و CSS3 إلى تحقيق هذا المقال، إذ يجب أن تُستخدم HTML حصرًا في هيكلة بناء المستند، بينما تُستخدم التنسيقات المورَّثة CSS للمظهر، من خطوط وألوان ومواضع وتحكم في رؤية العناصر أو إخفائها وغير ذلك، وبهذا تصبح برمجة الويب عملًا منظمًا يمكن صيانته، من خلال الجمع بين تلك الممارسات في كل من CSS وHTML مع أطر عمل الويب، وذلك من خلال تقنيتين رئيسيتين:

  1. توجيه نقطة النهاية endpoint routing للروابط، والتي يشار إليها أحيانًا بالربط mapping، حيث يوجَّه الجزء الأخير من الرابط من ملف إلى دالة أو تابع في إطار العمل.
  2. توفر قوالب المستندات طريقةً لإنشاء ملفات HTML ساكنة، فيها محددات للمواضع place-markers لإدراج البيانات فيها، ويمكن لدوال إطار عمل الويب أن تجهز البيانات ثم تمررها إلى محرك القالب، الذي يجلب القالب المناسب، ويدرج البيانات في محددات مواضعها لتوليد خرج HTML النهائي الذي يُعاد إلى متصفح المستخدم.

سنرى استخدام كلا التقنيتين في Flask، ولأطر عمل الويب غالبًا نفس المفاهيم، رغم اختلاف تفاصيل بنائها اللغوي syntax، واصطلاحات الاستدعاء المتبعة.

تثبيت Flask

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

عند استخدام pip نفتح طرفية النظام (وقد نضطر إلى تشغيل أمر pip بصلاحيات مدير النظام، خاصةً عند تثبيت بايثون لجميع المستخدمين على الحاسوب) ونكتب فيها ما يلي:

$ pip install flask

يمكن التحقق منها بتشغيل بايثون، واستيراد Flask بالأمر import flask، فإذا لم نحصل على أخطاء نكون قد نجحنا.

استخدام Flask في مثال Hello World

يوفر إطار عمل Flask وحدة خادم ويب بسيطةً، يمكن استخدامها للاختبار والتطوير قبل نقل الشيفرة إلى منصة استضافة ويب في شبكة تابعة لشركة أو على الإنترنت، وسندرس الآن كيفية تشغيله وتوفير صفحة ويب بسيطة.

صفحة ويب بسيطة

لن نحتاج هنا إلى إنشاء ملف HTML كما فعلنا في حالة CGI في المقال السابق، بل سنعيد شيفرة HTML من دالة Flask، كما يلي:

import flask

helloapp = flask.Flask(__name__)

@helloapp.route("/")
def index():
   return """
<html><head><title>Hello</title></head>
<body>
<h1>Hello from Flask!</h1>
</body></html>"""

if __name__ == "__main__":
    helloapp.run()

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

أما الأمر التالي فهو الدالة التي تعالج طلب http GET، والتي تسمى index اصطلاحًا لتوافق تسمية ملف index.htm، غير أن المثير للاهتمام هنا هو المزخرِف ‎@helloapp.route("/")‎ الذي يسبقها، والذي يخبر إطار Flask أن أي طلب إلى جذر الموقع / يجب توجيهه إلى التابع التالي المسمى index في حالتنا هذه، ويمكن توجيه عدة نقاط نهاية end points إلى نفس التابع بتكديس المزخرفات decorators فوق بعضها البعض، وقد ذكرنا من قبل أن توجيه نقاط النهاية هو إحدى التقنيات المستخدمة في أطر عمل الويب، وهذه هي طريقة إطار عمل Flask لتوجيهها.

تشغيل خادم ويب Flask

نبدأ تشغيل شيفرة Flask التي صارت جاهزةً الآن بالطريقة المعتادة، باستدعاء ما يلي من المجلد الذي يحوي التطبيق:

$ python hello.py

ينبغي أن نرى رسالةً تخبرنا أن الخادم يعمل وينتظر الطلبات:

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

نستطيع الآن العودة إلى المتصفح لندخل العنوان الموجود في الرسالة:

http://127.0.0.1:5000/

ستظهر رسالة "Welcome from Flask"، وبهذا نكون قد بنينا أول تطبيق ويب بإطار عمل Flask.

مقدمة في القوالب

تتعامل شيفرة إطار Flask التي رأيناها حتى الآن مع مشاكل توسع التطبيقات، وزيادة حجمها، وبعض الفوضى التي تسببها برمجة CGI، إلا أنها لا تستطيع مقال الشيفرة عن HTML، فلا زلنا نكتب سلاسل HTML داخل شيفرة البرنامج، ولمعالجة تلك المشكلة نستخدم قوالب Flask.

والقوالب في إطار Flask ما هي إلا ملفات HTML ساكنة فيها محددات markers أو مواضع خاصة لاستقبال البيانات من التطبيق، وهي تشبه سلاسل التنسيق format strings التي في بايثون، فمثلًا نريد قالبًا يستطيع استقبال رسالة الترحيب في سلسلة، ثم يدرجها في قالب، سيكون هذا القالب كما يلي:

<!doctype  html>
<head><title>Hello from Flask</title></head>
<body>
   <h1>{{message}}</h1>
</body>
</html>

إذا نظرنا إلى {{message}} فسنجد المواضع التي ذكرناها، فتلك الأقواس المزدوجة هي مواضع استقبال البيانات، والمتغير message هو اسم المتغير الذي سيدخله محرك القالب إلى HTML.

نحفظ المثال السابق في ملف باسم hello.htm داخل مجلد باسم templates في مجلد المشروع، واسم القالب مهم هنا لأنه المكان الذي يتوقع إطار Flask أن يجد فيه ملفات القوالب، يتبقى الآن بعض التعديلات على شيفرة بايثون الخاصة بنا لربطها بمحرك القوالب، وسنغير الشيفرة السابقة لتكون كما يلي:

from flask import Flask, render_template

helloapp = Flask(__name__)

@helloapp.route("/")
def index():
   return render_template("hello.htm", message="Hello again from Flask")

if __name__ == "__main__":
    helloapp.run()

الاختلاف الأول في تعليمة import، حيث غيرنا النمط لنستطيع استيراد الأسماء التي نحتاجها مباشرةً.

نلاحظ الآن اختفاء شيفرة HTML من ملف بايثون الخاص بنا، ونمرر الرسالة مثل سلسلة عادية إلى الدالة render_template مع اسم القالب، ونترك الباقي لإطار Flask، لكننا نريد أن نضمن مطابقة الوسائط المسماة في استدعاء هذه الدالة للأسماء التي في مواضع القالب، ثم نعرف كيف نقرأ البيانات الواردة من طلبات http لنستطيع تكرار نسخة CGI التي نفذناها من تطبيق "hello user".

استخدام Flask في مثال Hello User

نريد الآن تعديل التطبيق ليعمل مع استمارة HTML التي استخدمناها من قبل، فنبدأ بتحويل HTML إلى قالب نرسله عند تحديد المستخدم لنقطة نهاية "hello"، ولا نحتاج إلى إضافة أية محددات خاصة إلى HTML لأننا لا ندرج أي بيانات، لكن يجب تغيير الخاصية method للاستمارة إلى "POST"، وكذلك الخاصية action لتعكس منفذ إطار Flask -الذي هو 5000- ونقطة النهاية المطلوبة للرابط، والتي هي sayhello، ثم نحفظ ذلك في مجلد templates باسم helloform.htm ليستطيع إطار Flask العثور عليه، وسيبدو بعد تلك التعديلات كما يلي:

<!doctype html>
<html>
  <head>
    <title>Hello user page</title>
  </head>
  <body>
    <h1>Hello, welcome to our website!</h1>8000
    <form name="hello" 
          method="POST" 
          action="http://localhost:5000/sayhello">
      <p>Please tell us your name:</p>
      <input type="text" id="username" name="username" 
             size="30" required autofocus/>
      <input type="submit" value="Submit" />
    </form>
  </body>

كتابة شيفرة Flask

نحتاج الآن لإضافة تابعين جديدين إلى تطبيقنا، يعرض الأول منهما قالب helloform.htm، بينما يرد الثاني على طلب إرسال الاستمارة، ونلاحظ أن الدالة الثانية ستستخدم قالب hello.htm الأصلي، ولم نضف شيئًا سوى كتابة سلسلة رسالة جديدة:

from flask import Flask, render_template, request

helloapp = Flask(__name__)

@helloapp.route("/")
def index():
   return render_template("hello.htm", message="Hello again from Flask")

@helloapp.route("/hello")
def hello():
   return render_template("helloform.htm")

@helloapp.route("/sayhello", methods=['POST'])
def sayhello():
   name=request.form['username']
   return render_template("hello.htm", message="Hello %s" % name)

if __name__ == "__main__":
    helloapp.run()

لاحظ أننا اضطررنا إلى إضافة request إلى قائمة العناصر المستورَدة لأننا نستخدمه للوصول إلى بيانات طلب http، كما عدلنا مزخرف معالج sayhello ليشير إلى أنه يستطيع معالجة طلبات النوع POST -وقد كان الافتراضي طلبات GET- حيث نستخرج بيانات username داخل الدالة من الاستمارة، وندخلها في السلسلة التي نعيدها إلى قالب hello.htm. أما معالج hello فهو أبسط من ذلك، إذ يرسل قالب helloform.htm إلى المستخدم.

إذا شغّلنا الشيفرة الآن فسيعمل الخادم، وإذا كتبنا العنوان التالي:

http://localhost:5000/hello

فسنرى نفس الاستمارة التي كانت لدينا في مثال CGI من قبل (لاحظ أن كلمة localhost هي اسم بديل لـ 127.0.0.1، ويمكن تذكرها بسهولة أكثر)، فإذا ملأنا الاستمارة وضغطنا زر Submit؛ فستظهر لنا رسالة ترحيبية.

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

دليل جهات الاتصال

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

إعداد المشروع

لمشاريع إطار Flask عادةً بنية محددة، فهي تأخذ صورة هرمية مجلد، حيث يكون اسم المشروع في الأعلى ، متبوعًا بمجلد static للملفات الساكنة مثل الصور وملفات CSS، ومجلد templates للقوالب، وحزمة بايثون اسمها في الغالب هو اسم المشروع، والتي هي مجلد يحوي ملفًا اسمه ‎__init__.py يتحكم في ما تصدره الحزمة، كما تحتوي عادةً على ملف setup.py يُستخدم لتثبيت الحزمة إذا كانت موزَّعةً من فهرس حزم بايثون Python Package Index أو PyPI اختصارًا، وتسهل هذه الهرمية توزيع التطبيق باستخدام أدوات بايثون القياسية مثل pip.

لكننا لن نوزع التطبيق في حالة دليل جهات الاتصال، لذا سنكتفي بترتيب بسيط مبني على المجلدات القياسية الثلاثة، إضافةً إلى مجلد رابع لملف قاعدة البيانات:

addressbook
    static
    templates
    db

سننسخ ملف قاعدة البيانات من المقال السابق إلى مجلد db هنا.

إنشاء قالب HTML

سيكون لدينا صفحة ويب واحدة تتكون من استمارة بسيطة لإدخال البيانات، فيها ثلاثة أزرار هي Create وFind وList all، حيث سيستخدم زر Create البيانات التي في الاستمارة لإضافة مدخل جديد إلى قاعدة البيانات، ويعرض زر Find قائمةً بجميع العناوين المطابقة للبيانات التي في الاستمارة، بينما يعرض زر List all القائمة الكاملة للعناوين.

سنستخدم مزيةً جديدةً لمحرك القوالب، وهي القدرة على تكرار صفوف HTML وفقًا لمجموعة بيانات الدخل، وسيُدمج هذا مع عنصر HTML جديد هو وسم الجدول <table> وعائلته التي سنستخدمها لعرض قائمة العناوين.

وسيبدو القالب كما يلي:

<!doctype html>
<html>
  <head>
    <title>Flask Address Book</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
 
    <style>
      h1 {text-align: center;}
      label {
         width: 20em; 
         text-align: left; 
         margin-top: 2px; 
         margin-right: 2em; 
         float: left;}
      input {
         margin-left: 1em; 
         float: right; 
         width: 12em;}
      input.button {width: 6em; text-align: center; float: left;}
      br {clear: all;}
      div.buttons {float: left; width:100%; padding: 1em;}
    </style>
    
  </head>
 
  <body>
    <header>
    <h1>Address Book Website</h1>
    </header>
    <div class="content">
      <form name="hello" 
            method="POST" 
            action="http://localhost:5000/display">
      <fieldset><legend>Address</legend>
            <label>First name:
              <input type="text" id="First" name="First"
                required autofocus/>
            </label>
            <label>Second Name:
               <input type="text" id="Last" name="Last"
                      required />
            </label>
            <br />
            <label>House Number:
                   <input type="text" id="House" name="House" />
            </label>
            <label>Street:
                   <input type="text" id="Street" name="Street"
                          required />
            </label>
            <br />
            <label>District:
                   <input type="text" id="District" name="District" />
            </label>
            <label>Town:
               <input type="text" id="Town" name="Town"
                      required />
            </label>
            <br />
            <label>Postcode:
               <input type="text" id="Postcode" name="Postcode"
                      required />
            </label>
            <label>Phone:
               <input type="text" id="Phone" name="Phone" />
            </label>
      </fieldset>
      <div class="buttons">
           <input class="button" type="submit" value="List All"  />
           <input class="button" type="submit" name="Filter" value="Filter" />
           <input class="button" type="submit" name="Add" value="Add" />
           <input class="button" type="reset" value="Clear" />
      </div>
      </form>
      <br />
      <div class="data">
         <table id="addresses">
         <tr>
            <th>First</th>
            <th>Second</th>
            <th>House#</th>
            <th>Street</th>
            <th>District</th>
            <th>Town</th>
            <th>Postcode</th>
            <th>Phone</th>
         </tr>
         {% for row in book %}
         <tr>
            <td>{{row.First}}</td>
            <td>{{row.Last}}</td>
            <td>{{row.House}}</td> 
            <td>{{row.Street}}</td>
            <td>{{row.District}}</td>
            <td>{{row.Town}}</td>
            <td>{{row.PostCode}}</td>
            <td>{{row.Phone}}</td>
         </tr>
         {% endfor %}
         </table>
      </div>
  </div>

  </body>
</html>

يوجد عدد كبير من العناصر الجديدة هنا، بما في ذلك قسم <style> الذي يتحكم في تخطيط الاستمارة، ويمكن الرجوع إلى أي شرح للغة CSS -مثل توثيق CSS في موسوعة حسوب- لمعرفة ما تفعله الشيفرة هنا إذ لا يتسع المقام لشرحها بسبب بعدها عن الغرض من المقال.

كما توجد بعض مزايا HTML الجديدة:

  • يوضع ترميز المحارف في وسم meta داخل وسم head، وتجب إضافة هذا الوسم في صفحات الويب الحديثة خاصةً إذا كنا نستخدم محارف غير قياسية، وإلا فسيكون لدينا في الصفحة أجزاء غير قابلة للقراءة.
  • الوسم meta هو منفذ للعرض viewport، وهو مصمم لتحسين عرض الصفحة على الأجهزة المحمولة، ويُفضل إضافة هذا الوسم إلى الصفحة.
  • استخدمنا عددًا من وسوم div، وهي مجرد أدوات تنظيمية لا تظهر على الشاشة إلا إذا تعمدنا إظهارها باستخدام CSS، وهي تشبه ودجات Frame التي استخدمناها في برامج Tkinter الرسومية، وتفيدنا في التحكم في نطاق الأوامر لكل من CSS وجافاسكربت.
  • تحتوي الاستمارة form على ثلاثة عناصر جديدة، يساهم كل منها في تحسين مظهرها، هي العنصر fieldset الذي يجمع عدة حقول داخل إطار مرئي يُدرج فيه العنصر legend، والعنصر label الذي يغلف حقول input ليجعلها معًا على الشاشة.
  • نختم الاستمارة بثلاثة أزرار من النوع submit لها الخاصية name التي تُرسَل إلى الخادم، ونستطيع استخدام تلك البيانات لتحديد الدالة التي يجب تنفيذها، أما الزر الأخير فيكون من النوع reset، وهو يمسح حقول الاستمارة ولا يرسل شيئًا إلى الخادم.
  • قسم <table> الذي يبدأ بصف من الترويسات <th> و<tr>.
  • يوفر الجزء التالي ميزةً جديدةً لمحرك القوالب، وهي القدرة على تنفيذ حلقات تكرارية واستبدال عدة أجزاء من البيانات، وهي الحقول التي في كل صف في حالتنا، وتُحدَّد بنى التحكم هذه بمحارف {%...%}، ويدعم المحرك عدة بنىً مختلفة إضافةً إلى الحلقة الموضحة هنا.

كتابة شيفرة إطار Flask

لا نحتاج إلى إضافة الكثير من الشيفرات الجديدة في هذا المشروع، فعناصر إطار Flask مجرد توسيع طفيف من المثال السابق لتحديد أي زر من أزرار submit الثلاثة قد ضُغط، وبمجرد أن ننفذ ذلك نستدعي دوال بايثون العادية التي تعدل قاعدة البيانات باستخدام وحدة sqlite3 كما شرحنا في مقال قواعد البيانات، وتعيد تلك الدوال قائمةً من عناصر القواميس، بمقدار عنصر واحد لكل صف في قاعدة البيانات، وتمرَّر تلك القائمة إلى محرك عرض القوالب template rendering engine الذي ينفذ مهمة إدخال HTML إلى الصفحة التي أنشئت، وستبدو الشيفرة كما يلي:

from flask import Flask, render_template, request, g
import sqlite3, os

addBook = Flask(__name__)
addBook.config.update(dict(
   DATABASE=os.path.join(addBook.root_path,'static','address.db'),
   ))

@addBook.route("/")
def index():
   data = findAddresses()
   return render_template("address.htm", book=data)

@addBook.route("/display", methods=['POST'])
def handleForm():
   if 'Filter' in request.form:
      query= buildQueryString(request.form)
      return render_template('address.htm', book=findAddresses(query))
   elif 'Add' in request.form:
      addAddress(request.form)  # add a new entry
   return render_template("address.htm", book=findAddresses())

# Note: flask.g is the "global context" object
# that lives for the life of the application.
def get_db():
    if not hasattr(g, 'sqlite_db'):
       try:
          file = addBook.config['DATABASE']
          db = sqlite3.connect(file)
          db.row_factory = sqlite3.Row  # return dicts instead of tuples
       except: print("failed to initialise sqlite")
       g.sqlite_db = db
    return g.sqlite_db

@addBook.teardown_appcontext
def close_db(error):
    if hasattr(g, 'sqlite_db'):
         db = g.sqlite_db
         db.commit()
         db.close
                    
def buildQueryString(aForm):
    base = "WHERE"
    test = " %s LIKE '%s' "
    fltr = ''

    for name in ('First','Last',
                 'House','Street',
                 'District','Town',
                 'PostCode','Phone'):
       field = aForm.get(name, '')
       if field:
           if not fltr:  fltr = base + test % (name, field)
           else: fltr = fltr + ' AND ' + test % (name, field)
    return fltr 

def findAddresses(filter=None):
    base = """
SELECT First,Last,House,Street,District,Town,PostCode,Phone
FROM address %s
ORDER BY First;"""

    db = get_db()
    if not filter: filter = ""  # empty -> get all
    query = base % filter
    cursor = db.execute(query)
    data = cursor.fetchall()
    return data

def addAddress(aForm):
    db = get_db()
    cursor = db.cursor()
    
    first   = aForm.get('First','')
    last    = aForm.get('Last','')
    house   = aForm.get('House','')
    street  = aForm.get('Street','')
    district= aForm.get('District','')
    town    = aForm.get('Town','')
    code    = aForm.get('PostCode','')
    phone   = aForm.get('Phone','')

    query = '''INSERT INTO Address 
               (First,Last,House,Street,District,Town,PostCode,Phone)
               Values ("%s","%s","%s","%s","%s","%s","%s","%s;");''' %\
               (first, last, house, street, district, town, code, phone)
    cursor.execute(query)
   
if __name__ == "__main__":
   addBook.run()

نلاحظ بعض النقاط المهمة هنا:

  • نستورد الاسم g من إطار Flask، وهو كائن خاص يوفره Flask ليحمل قيم مستويات التطبيق، ونستفيد من المبدأ الذي يقوم عليه، رغم غرابة اسمه، وهو يشبه المعامِل self في البرمجة الكائنية لكنه ينطبق هنا على تعاملات الويب بدلًا من الكائن.
  • نعِدّ موقع قاعدة البيانات في البنية الخاصة addbook.config، وهناك عدة عناصر أخرى يجب إعدادها هنا في الموقع الكامل، بما في ذلك اسم المستخدم وكلمة المرور للمدير admin، وتخزَّن هذه الأمور عادةً في ملف config، ويُستخدم أمر خاص لتحميلها. كما يجب الانتباه إلى خيار DEBUG الذي يؤدي لطبع معلومات كثيرة إلى الطرفية عند ضبطه على القيمة True، لأن هذه المعلومات مفيدة للغاية في حال حدوث المشاكل أثناء التطوير.
  • تتحقق الدالة get_db من كون الخاصية sqlite_db مضبوطةً أو لا قبل أن تضبطها، وهذا يضمن وجود اتصال واحد فقط لقاعدة البيانات.
  • كما تضبط هذه الدالة row_factory من sqlite3.Row، وفائدته أنه يجعل Sqlite تعيد قائمةً من كائنات Row بدلًا من صفوف tuple تحتوي على قيم، وتُعامَل كائنات Row تلك على أنها قواميس، وهو ما يبحث عنه Flask تحديدًا في محرك القوالب الخاص به، لذا يوفر سطر الشيفرة ذاك علينا كثيرًا من تنسيق البيانات.
  • ينبغي أن توجد معالجات أخطاء try/except حول الكثير من التعليمات البرمجية، خاصةً أقسام قواعد البيانات، لكننا لم نشأ الإطالة أكثر من اللازم، وسنرى مثالًا في دالة get_db يوضح أننا نستطيع استخدام تعليمات print لعرض خرج التنقيح debug output في الطرفية.
  • يُستخدم هنا مزخرِف جديد هو ‎@addBook.teardown_appcontext لرفع راية إلى إطار Flask بأنه يجب تشغيل الدالة close_db عند إغلاق التطبيق.
  • تساعد الدالة buildQueryString في بناء شرط WHERE الخاص باستعلامنا ديناميكيًا، وقد تبدو معقدةً إلا أنها مجرد تعديل بسيط على سلسلة نصية، ويسمح لنا هذا الأسلوب باستخدام دالة واحدة لإيجاد جميع العناوين عند عدم استخدام مرشحات filters، أو العناوين المطلوبة فقط.
  • تكون عمليات البحث أكثر مرونةً باستخدام عامل LIKE الخاص بـ SQL، بدلًا من اختبار التكافؤ equality test، بما في ذلك القدرة على استخدام علامة % محرف بدل في SQL.
  • نستخدم تابع القاموس get لجلب القيم من الاستمارة، وهذا يضمن حصولنا على قيمة افتراضية عند عدم وجود المفتاح لسبب ما، وهي سلسلة فارغة في حالتنا.

تشغيل دليل جهات الاتصال

سنغير مجلد التطبيق ونشغل الملف address.py، ثم نستخدم المتصفح لزيارة localhost:5000، وبهذا تطابق عملية التشغيل هذه عملية التشغيل السابقة، ويجب أن نرى نفس الأسماء والعناوين التي أنشأناها في مقال قواعد البيانات في المتصفح.

كما ينبغي أن نستطيع الآن إدخال القيم في الاستمارة، وترشيح النتائج لمطابقة كلمات البحث -نستخدم % محرف بدل wildcard- وكذلك نستطيع إضافة إدخالات جديدة، وبما أنه لا يوجد تحقق من الحقول أو الاستمارة فيجب أن نراعي إدخال قيم منطقية مناسبة، أو نتلافى أي أخطاء يدويًا باستخدام محث sqlite3.

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

خاتمة

نأمل في نهاية هذا المقال أن تكون تعلمت ما يلي:

  • تحسّن أطر عمل الويب إمكانية توسيع المواقع وصيانتها.
  • توجه أطر عمل الويب نقاط النهاية إلى الدوال.
  • توجد عدة إطارات عمل للويب في السوق الآن.
  • إطار Flask يمثل حلًا وسطًا بين البساطة والإمكانيات.
  • يستخدم Flask مزخرفات decorators لتوجيه نقاط النهاية.
  • كما يستخدم نظام قوالب لتمرير البيانات إلى صفحات الويب.

ترجمة -بتصرف- للمقال الحادي والثلاثين: Using Web Apllication Frameworks من كتاب Learn To Program لصاحبه Alan Gauld.

اقرأ أيضًا


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

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

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



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...