أدوات المستخدم

أدوات الموقع


okasha:elixir

اختلافات

عرض الاختلافات بين النسخة المختارة و النسخة الحالية من الصفحة.

رابط إلى هذه المقارنة

جانبي المراجعة السابقة المراجعة السابقة
المراجعة التالية
المراجعة السابقة
okasha:elixir [2010/04/30 17:52]
alsadi
okasha:elixir [2015/04/23 00:21] (حالي)
سطر 1: سطر 1:
 +====== قواعد البيانات عبر الإكسير ======
 +===== مقدمة =====
 +يمكن استعمال ربط تطبيقات عكاشة مع قواعد البيانات مثل sqlite (دعمها مدمج في بايثون) أو mysql عبر حزم بايثون لهذا الغرض (مثل [[http://​code.google.com/​p/​pymysql/​|pymysql]])
 +
 +لكن في هذه الحالة يكون عليك متابعة عدد من الأمور مثل:
 +  * تكون محصور في قاعدة بيانات بعينها (مثلا سيكون من الصعب نقل الكود من SQLite إلى MySQL أو PostgreSQL)
 +  * عليك القيام يدويا بتحسين تعابير SQL
 +  * في sqlite لا تستطيع التحدّث مع اتصال فتحته في thread آخر ممّا يعني أنك قد تحتاج لعمل اتصال لكل طلب.
 +
 +لذا هناك بديل لهذه الطريقة اسمه [[http://​www.sqlalchemy.org/​|SQLAlchemy]] وحوله بُني [[http://​pypi.python.org/​pypi/​Elixir/​|الإكسير]] لتسهيله.
 +
 +الإكسير عبارة عن ORM أي  Object-relational mapping حيث تتحوّل الجداول والصفوف وعلاقاتها إلى كائنات.
 +
 +[[http://​www.sqlalchemy.org/​docs/​dbengine.html?​highlight=engines#​supported-databases|تدعم]] SQLAlchemy عددا كبيرا من قواعد البيانات من بينها:
 +  * Sqlite
 +  * MySQL
 +  * Oracle
 +  * Postgresql
 +
 +إنّ الإكسير يقوم بالكثير من التحسينات فهو مثلا يعمل اتصال واحد مع قاعدة البيانات إلاّ في حالة Sqlite يقوم بعمل اتصال لكل thread
 +
 +===== الإكسير من خارج عكاشة =====
 +==== عمل المخطط ====
 +لنفترض أن لدينا قاعدة بيانات للكتب تتكوّن من جدول للمؤلفين وجدول للكتب وجدول للناشرين وجدول للتصنيفات من خلال keywords.
 +
 +جدول الكتب يتكوّن من الحقول التالية:​
 +  * معرف رقمي فريد تلقائي
 +  * اسم الكتاب وهو نص قصير
 +  * وصف الكتاب وهو نص حر طويل
 +  * عام النشر
 +  * معرف ISBN فريد ومفهرس
 +  * المؤلّفون وهي علاقة كثير لكثير ​ many to many مع جدول المؤلفين يعني ممكن أن يكون أكثر من مؤلف لنفس الكتاب أو للمؤلف الواحد أكثر من كتاب
 +  * الناشر وهي علاقة كثير لواحد أي قد يكون لأكثر من كتاب ناشر واحد
 +  * التصنيفات وهي علاقة كثير لكثير.
 +
 +جدول المؤلفون حيث يتكوّن من الحقول التالية:​
 +  * معرف رقمي فريد تلقائي
 +  * اسم المؤلف وهو نص
 +  * عام الميلاد
 +  * عام الوفاة أو صفر للأحياء
 +  * الكتب وهي علاقة كثير لكثير (عكس علاقة المؤلفون في جدول الكتب)
 +
 +جدول الناشرون
 +  * معرف رقمي فريد تلقائي
 +  * اسم الناشر وهو نص
 +  * الكتب وهي علاقة واحد لكثير (عكس علاقة الناشر في جدول الكتب)
 +
 +جدول التصنيفات بالكلمات المفتاحية
 +  * معرف رقمي
 +  * الكلمة
 +  * الكتب وهي علاقة كثير لكثير
 +
 +==== تنفيذ المخطط ====
 +
 +لنكتب وحدة module تُعرّف هذه القاعدة كما يلي ونحفظها في ملف باسم bookstoreModel.py
 +<code python>
 +from elixir import *
 +
 +class Book(Entity):​
 +  using_options(tablename="​book"​)
 +  n = Field(Integer,​ primary_key=True,​ autoincrement=True)
 +  title = Field(Unicode(30),​ index=True)
 +  description = Field(UnicodeText)
 +  year = Field(Integer)
 +  isbn = Field(String(16),​ index=True, unique=True)
 +  authors = ManyToMany('​Author',​ lazy=False)
 +  publisher = ManyToOne('​Publisher',​ lazy=False)
 +  keywords = ManyToMany('​Keywords',​ lazy=False)
 +
 +class Author(Entity):​
 +  using_options(tablename="​author"​)
 +  n = Field(Integer,​ primary_key=True,​ autoincrement=True)
 +  name = Field(Unicode(30),​ index=True)
 +  details = Field(UnicodeText)
 +  year_of_birth = Field(Integer,​ index=True)
 +  year_of_death = Field(Integer,​ index=True, default=0)
 +  books = ManyToMany('​Book',​ lazy=True)
 +
 +class Publisher(Entity):​
 +  using_options(tablename="​publisher"​)
 +  n = Field(Integer,​ primary_key=True,​ autoincrement=True)
 +  name = Field(Unicode(30),​ index=True)
 +  books = OneToMany('​Book',​ lazy=True)
 +
 +class Keywords(Entity):​
 +  using_options(tablename="​keywords"​)
 +  n = Field(Integer,​ primary_key=True,​ autoincrement=True)
 +  name = Field(Unicode(30),​ index=True)
 +  books = ManyToMany('​Book',​ lazy=True)
 +
 +</​code>​
 +
 +في نفس المجلد افتح بايثون التفاعلي واكتب فيه
 +<code python>
 +from bookstoreModel import Book, Author, Publisher, Keywords
 +import elixir
 +elixir.metadata.bind='​sqlite:///​db.sqlite'​
 +elixir.setup_all()
 +elixir.create_all()
 +</​code>​
 +
 +الأمر الأخير create_all يعمل على إنشاء قاعدة البيانات (نستدعيه مرة واحدة حيث لاحقا نكتفي ب setup_all) وهي في مثالنا نتحدث مع قاعدة بيانات من نوع sqlite وهي موجودة في الملف db.sqlite لاحظ سطر bind
 +
 +يمكنك طبعا استعمال mysql وتقديم اسم مستخدم وكلمة سر واسم القاعدة ...إلخ مثلا ​
 +<code python>
 +elixir.metadata.bind='​mysql://​user:​pass@localhost/​mytestdb'​
 +</​code>​
 +
 +لكن دعونا نتابع مستخدمين ملف sqlite وليس mysql. الآن اخرج من مفسر بايثون التفاعلي وانظر محتويات ملف db.sqlite عبر الأمر التالي:​
 +<code bash>
 +sqlite3 db.sqlite .dump | less
 +</​code>​
 +
 +ستلاحظ مخطط قاعدة البيانات schema دون مدخلات لأنها لا تزال خالية.
 +
 +لنعد إلى مفسر بايثون التفاعلي ونفتح قاعدة البيانات بالأوامر
 +<code python>
 +from bookstoreModel import Book, Author, Publisher, Keywords
 +import elixir
 +elixir.metadata.bind='​sqlite:///​db.sqlite'​
 +elixir.setup_all()
 +</​code>​
 +
 +في مراحل الاختبار يمكنك إظهار عمليات SQL حتى تراها بعينك وذلك بكتابة السطر
 +
 +<code python>
 +elixir.metadata.bind.echo = True
 +</​code>​
 +
 +==== إضافة المدخلات ====
 +
 +لنقم الآن بإدخال بعض الحقول في قواعد البيانات. ولنبدأ بعمل حقل في جدول الكلمات المفتاحية وذلك عبر الأمر
 +
 +<code python>
 +kw_s=Keywords(name=u"​science"​)
 +kw_a=Keywords(name=u"​art"​)
 +p1=Publisher(name=u"​My Press"​)
 +p2=Publisher(name=u"​Nashir2"​)
 +elixir.session.commit()
 +</​code>​
 +
 +السطر الأول يعمل كائن من نوع Keywords ويعين خصائصه والتي هي name ويعطيها قيمة science وهذا يعني عمل صف في جدول keywords قيمة العمود name فيه هي science. يتم حفظ هذا الكائن في المتغير kw_s وهذا غير إلزامي لكنني فعلته لأني قد استخدمه لاحقا.
 +
 +السطر الثاني عمل كائنا آخر وبالتالي صف آخر بقيمة أخرى. ثم علمنا ناشرين بنفس الطريقة. ولو قمت الآن بفتح قاعدة البيانات لن تجد فيها شيء ذلك أن الإكسير يجمع العمليات إلى أن تطلب منه إرسالها محسنة عبر الدالة elixir.session.commit.
 +
 +لنضف الآن عدد من المؤلفين
 +
 +<code python>
 +a1=Author(name=u"​Oqlah ibn Khalaf",​ details=u"​short story pioneer in Balama",​ year_of_birth=1975)
 +a2=Author(name=u"​John Random Hacker",​ details=u"​fictional author from cyberspace",​ year_of_birth=1980)
 +elixir.session.commit()
 +</​code>​
 +
 +لنضف الآن أحد الكتب
 +<code python>
 +b1=Book(title=u"​The twilight of Balama",​ description=u"​a short story.",​ year=2010, isbn="​0-1234-5678-9",​ publisher=p1)
 +b1.authors.append(a1)
 +b1.keywords.append(kw_a)
 +
 +b2=Book(title=u"​Facts you don't know", description=u"​true stories from a fake author",​ year=2010, isbn="​1-1234-5678-9",​ publisher=p2)
 +b2.authors.append(a2)
 +b2.keywords.append(kw_s)
 +
 +elixir.session.commit()
 +</​code>​
 +لاحظ استخدام append لإضافة العلاقات. طبعا يمكنك استخدام الاستعلامات لتوليد الكائنات التي تمررها إلى append عوضا عن حفظ الكائنات في متغيرات
 +
 +==== الاستعلام والتعديل ====
 +
 +أغلق جلسة بايثون التفاعلي السابقة ولنبدأ واحدة جديدة ونكتب بدايتنا التقليدية
 +<code python>
 +from bookstoreModel import Book, Author, Publisher, Keywords
 +import elixir
 +elixir.metadata.bind='​sqlite:///​db.sqlite'​
 +elixir.setup_all()
 +</​code>​
 +
 +لنستعلم عن شيء له معرف فريد أو فهرس فريد مثلا
 +<code python>
 +b=Book.get_by(isbn="​1-1234-5678-9"​)
 +print b.title
 +</​code>​
 +لاحظ استخدام get_by على الفئة نفسها دون كائن منها (يعرف هذا class method أو static method)
 +مع تمرير لها معرف isbn للحصول على كائن الكتاب.
 +وباملناسبة يمكننا التعديل على الكائن مباشرة حيث تعكس تلك التعديلات على قاعدة البيانات فور عمل commit مثلا لنفرض أني أريد زيادة علامة تعجب في نهاية العنوان
 +
 +<code python>
 +b.title+=u"​!"​
 +elixir.session.commit()
 +</​code>​
 +
 +إن العلاقات تعمل بشكل تلقائي يعني لو استعلمنا عن الكتب تحت تصنيف art يمكننا طباعة أسماءها وأسماء مؤلفيها
 +<code python>
 +kw=Keywords.get_by(name="​art"​)
 +for b in kw.books:
 +  print "book: ",​b.title,",​ authors: "
 +  for a in b.authors:
 +    print b.name,
 +  print "​."​
 +</​code>​
 +
 +==== الاستعلام والفلترة ====
 +بطريقة مشابهة لنفرض أننا نريد كل الكتب التي طبعت بعد 1960
 +<code python>
 +books=Book.query.filter(Book.year>​1960)
 +for b in books: print b.title, "​@",​ b.year
 +</​code>​
 +يمكننا أيضا تركيب الفلاتر وراء بعضها كذلك يمكننا ترتيب النتائج وأخذ أول كذا نتيجة
 +
 +<code python>
 +books=Book.query.filter(Book.year>​1960).order_by(elixir.sqlalchemy.desc(Book.year)).limit(10)
 +for b in books: print b.title, "​@",​ b.year
 +</​code>​
 +
 +
 +===== مثال على ربطها مع عكاشة =====
 +
  
okasha/elixir.txt · آخر تعديل: 2015/04/23 00:21 (تحرير خارجي)