إن غالبية دوال وصنوف عكاشة موثّقة جيّداً لذا يمكنك استعمال pydoc للوصول لها.
كذلك يمكنك استعمال المثال test.py الموجود في كود عكاشة فهو يوضّح أغلب استخدامات عكاشة.
بعكس ذاك المثال المسهب فإن هذه الوثيقة ستعلّمك كيف تعمل مشروع بدهي مثل “مرحبا، يا عالم!”
وقبل أن تبدأ تخلّص من الخوف فأغلب الكود هو تعليقات (مجرد شرح).
أنشء مجلّد للمشروع. وإن لم تكن الاعتماديات1) مثبّتة في النظام أو كنت تريد أن يكون التطبيق الخاص بك قابل للنقل portable إلى أجهزة لا تضمن أن يكون عكاشة وبقية الاعتماديات مثبّتا فيها كل ما عليك هو وضع نسخة من تلك الحزم داخل مجلد المشروع الحالي.
بعكس أطر الويب الأخرى تطبيق الويب هو مجرّد ملف واحد به صنف واحد لذا اعمل ملف سمّه مثلا myapp.py ولتكن محتوياته كما يلي:
#! /usr/bin/python # -*- coding: UTF-8 -*- """ this is my first okasha web app """ from okasha.baseWebApp import * class MyApp(baseWebApp): def __init__(self, *args, **kw): baseWebApp.__init__(self,*args, **kw)
وبهذا نكون قد أنهينا أول تطبيق لنا
هناك عدّة طرق لإطلاق تطبيقات عكاشة. أسهلها وأفضلها خلال فترة التطوير هو استعمال حزمة python-paste
يمكنك عمل ملف منفصل اسمه serve.py مثلا يقوم على تشغيل التطبيق الخاص بك وذلك كما يلي
#! /usr/bin/python # -*- coding: UTF-8 -*- import sys, os, os.path # this requires python-paste package from paste import httpserver from myapp import MyApp d=os.path.dirname(sys.argv[0]) application=MyApp( os.path.join(d,'templates'), staticBaseDir={'/media/':os.path.join(d,'media')} ); httpserver.serve(application, host='127.0.0.1', port='8080')
فعند تنفيذ برنامج serve.py أعلاه سيعمل خادم paste الضمني المنضدد على المنفذ 8080 (يمكنك تحديد أي رقم) وسيقبل أي اتصالات من العنوان المحلي 127.0.0.1 . إن كنت تريده أن يستقبل من كل العناوين اجعله 0.0.0.0 للمزيد من الخيارات انظر وثائق paste
بل ويمكن وضع هذا الكود داخل الوحدة التي تحتوي تطبيق الويب نفسه يعني في مثالنا يمكن الاستغناء عن serve.py وذلك بأن نجعل ملف myapp.py كما يلي:
#! /usr/bin/python # -*- coding: UTF-8 -*- """ this is my first okasha web app """ from okasha.baseWebApp import * class MyApp(baseWebApp): def __init__(self, *args, **kw): baseWebApp.__init__(self,*args, **kw) if __name__ == '__main__': # this requires python-paste package from paste import httpserver import sys, os, os.path d=os.path.dirname(sys.argv[0]) application=MyApp( os.path.join(d,'templates'), staticBaseDir={'/media/':os.path.join(d,'media')} ); httpserver.serve(application, host='127.0.0.1', port='8080')
وفي هذه الحالة لا نحتاج ملف serve.py حيث يمكننا إطلاق تطبيق الويب عبر تنفيذ myapp.py حيث أن الجزء في آخر الملف لن ينفّذ في حالة استخدام الملف كوحدة بايثونية (مكتبة) بل عند تنفيذه كبرنامج وليس مكتبة.
في أول البرنامج أول خطوة قمنا بها هي استيراد محتويات إطار الويب عكاشة
from okasha.baseWebApp import *
ثم قمنا بتعريف كائن مشتق من تطبيق الويب الأساسي في عكاشة baseWebApp
class MyApp(baseWebApp):
وفيه عرّفنا دالة الاستهلال التي كل مهمّتها هي استدعاء الدالة في الصنف الأساس وتمرير المعاملات له بشكل أعمى (سنشرح هذه المعاملات لاحقا)
def __init__(self, *args, **kw): baseWebApp.__init__(self,*args, **kw)
يمكنك استقبال معاملات خاصة بك هكذا للاحتفاظ بها لاستخدامها لاحقا هكذا
def __init__(self, name, *args, **kw): self.name=name baseWebApp.__init__(self,*args, **kw)
أما كود التشغيل فهو كما يلي:
from paste import httpserver
نستورد خادم http من داخل حزمة paste. يستقبل خادم paste تطبيق الويب المتوافق مع WSGI كمعامل أول هكذا
httpserver.serve(application, host='127.0.0.1', port='8080')
حيث application هو تطبيق الويب وهو في حالتنا كائن فرد من الصنف MyApp الذي اشتققناه من تطبيق الويب الأساسي في عكاشة baseWebApp
نقوم بعمل هذا الكائن كما نعمل أي متغير أو كائن في بايثون
d=os.path.dirname(sys.argv[0]) application=MyApp( os.path.join(d,'templates'), staticBaseDir={'/media/':os.path.join(d,'media')} );
المتغير d يمثّل المجلّد الذي يحتوي تطبيق الويب وقد قمنا بتحديد مجلد فرعي عنه اسمه templates كي يحتوي القوالب كذلك طلبنا أن تحال كل العناوين التي تبدأ ب /media/ إلى المجلد الفرعي media داخل مجلد التطبيق حيث يمكننا استخدامه لوضع ملفات الصور وملفات css وغيرها من الملفات الساكنة static files.
يوفّر عكاشة أكثر من طريقة لتخديم الملفات الساكنة منها أن تمرّر له قاموس staticBaseDir مفاتيحه هي الجزء في بداية العنوان ثم المجلد الذي سيتم البحث عن الملفات فيه (يمكن استخدام أكثر من مجلد)
كذلك هناك خاصية أخرى وهي عمل إعادة توجيه redirect لطلبات معيّنة إلى عنوان آخر أو خادم آخر يحتوي تلك الملفات الساكنة.
للمزيد انظر وثائق دالة الاستهلال لصنف baseWebApp في كود عكاشة.
سيبحث عكاشة عن الطريقة/الدالة في كائن التطبيق فإن لم يجد واحدة مناسبة فإنه يستدعي الدالة _root ويمرر لها كائن من الصنف Request يحتوي تفاصيل الطلب ويمرّر له بقية عناصر العنوان. وحيث أنّنا لم نعرّف تلك الدالة فإن دالة _root التلقائية الموجودة في عكاشة تعمل وتظهر لنا في شاشة المتصفح عند زيارة الموقع صفحة html تحتوي
You requested [/]
فإن زرنا العنوان http://localhost:8080/hello/omar سيظهر في المتصفح
You requested [/hello/omar]
جرّب إضافة الكود التالي بعد دالة الاستهلال init في صنف MyApp
@expose() def _root(self, rq, *args): return """<html><body> <img src="/media/logo.gif"/> <h1>welcome to my first okasha application</h1> </body></html>"""
الكثير من النّاس لا يألَفون استخدام @ أو ما يُسمى decorator وشرحها في هذه المرحلة غير مناسب لكن اعلم أنها تستدعي دالة خاصة وهي expose موجودة داخل عكاشة تقوم هذه الدالة بتغيير الدالة التي بعدها وهي في مثالنا _root
يمكنك أن ترى وثائق دالة expose في كود عكاشة حتى تعرف ما هي المعاملات التي تستقبلها لكن إن استعملتها دون أي معاملات كما فعلنا فهي تعني أننا لن نستعمل أي قوالب بل سنعيد كود html
لاحظ أننا استعملنا ملف logo.gif الموجود في media لكن الرابط الذي استخدمناه قد لا يكون صحيحا دائما لأنه إن كان التطبيق يعمل من داخل سابقة ما حيث لا يكون التطبيق مربوط على جذر الخادم بل داخل سابقة هكذا http://myhost/myapp ويكون ذلك مفيدا عندما يكون هناك أكثر من تطبيق على نفس النطاق domain
وحتى لا تعيد كتابة الروابط يمكنك استخدام rq.script التي تعيد سابقة التطبيق الخاص بنا (طبعا ستكون نص خالي إن لم يكن هناك سابقة)
@expose() def _root(self, rq, *args): return """<html><body> <img src="%s/media/logo.gif"/> <h1>welcome to my first okasha application</h1> </body></html>""" % rq.script
@expose() def hello(self, rq, *args): if not args: s="world" else: s=args[0] return """<html><body> <h1>Hello, %s!</h1> </body></html>""" % s @expose() def _root(self, rq, *args): if not args: raise redirectException(rq.script+'/hello/') elif args[0]=='favicon.ico': redirectException(rq.script+'/media/favicon.ico') else: raise forbiddenException()
في هذا المثال عملنا دالة اسمها hello سيتم تنفيذ هذه الدالة عند زيارة /hello/ أو /hello/Name/ أو ما شابه. هذه الدالة تستلم بقية العنوان على شكل معاملات حيث ستكون قيمة args منظومة خالية [] أو ['Name'] على الترتيب في المثالين.
كذلك جعلنا الوصول لجذر التطبيق يحيل إلى العنوان /hello/ الذي بدوره يطبع Hello, world! لأنه لا يوجد تتمة للعنوان (المنظومة خالية)
كذلك عملنا تحويلة أخرى داخل _root تحيل من favicon.ico إلى /media/favicon.ico
أمّا إن لم يجد دالة مناسبة ولم يحصل أي من الحالتين السابقتين (أي لم نطلب جذر التطبيق ولا favicon) عندها سينفذ ما بعد else وهو في حالتنا رفع الخطأ رقم 403 ممنوع الوصول لتلك الصفحة.
يمكنك مكان السطر الأخير كتابة ما يلي
else: raise fileNotFoundException()
حتى تحصل على الخطأ 404 (صفحة غير موجودة)
كذلك يمكنك تخصيص الصفحة التي تظهر عند حدوث هذين الخطأين عبر تخصيص الدالة _403 و _404
غيّر الدالة hello لتصبح كما يلي :
@expose() def hello(self, rq, *args): n=rq.q.getfirst('n','world').decode('utf-8') return """<html><body> <h1>Hello, {0}!</h1> <form name="MyForm"> Please enter name: <input name="n" type="text" value="{0}"></input> </form> </body></html>""".format(n)
لاحظ استخدامنا ل rq.q.getfirst للحصول على قيمة عنصر من النموذج وهو في حالتنا n وهي ذاتها التي تأتي من مربع النص الذي اسمه n في كود html. إن لم تكن تلك القيمة التلقائية world مكانها