تُعد SQLite قاعدة بيانات SQL قائمة بحد ذاتها self-contained، ومعتمدة على الملفات file-based، وهي مُضمّنة في بايثون افتراضيًا، إذ من الممكن استخدامها في أي من تطبيقات بايثون دون الحاجة لتثبيت أي برمجيات إضافية.
سنتعرف في هذا المقال على الوحدة sqlite3
في بايثون 3، إذ سننشئ اتصالاً مع قاعدة بيانات SQLite، كما سنضيف جدولاً إليها، وسندخل بعض البيانات إلى هذا الجدول ونقرأها ونعدّلها.
سنتعامل في هذا المقال مع مثال لتطبيق جرد مخزون أسماك في حوض مُفترض، وبالتالي لا بُدّ من التعديل في حال إضافة أو إزالة سمكة من الحوض.
مستلزمات العمل
لتحقيق أقصى فائدة ممكنة من هذا المقال، يُفضّل أن تكون مُطّلعًا على البرمجة بلغة بايثون وعلى أساسيات لغة SQL، وفي هذا الصدد ننصحك بقراءة المقالات التالية قبل إكمال هذا المقال:
الخطوة الأولى – إنشاء اتصال مع قاعدة بيانات SQLite
عند إنشاء اتصال مع قاعدة بيانات SQLite، فإنّنا نصل بالنتيجة إلى بيانات موجودة في ملف ما على جهاز الحاسوب، حيث تُعد محركات قواعد بيانات SQL كاملة الميزات ويمكن استخدامها لأغراض متعددة، وسنتعامل في مقالنا الحالي مع قاعدة بيانات تُعنى بتعقّب مخزون الأسماك في حوض أسماك مُفترض.
يمكن الاتصال بقاعدة البيانات SQLite باستخدام الوحدة sqlite3
في بايثون على النحو التالي:
import sqlite3 connection = sqlite3.connect("aquarium.db")
حيث تستورد التعليمة import sqlite3
الوحدة sqlite3
، مما يمنح برنامج بايثون وصولًا إلى هذه الوحدة، بينمّا تعيد الدالة ()sqlite3.connect
كائن اتصال Connection
والذي سنستخدمه في التخاطب مع قاعدة البيانات SQLite الموجودة في الملف aquarium.db
الذي يُنشأ تلقائيًا من قبل الدالة ()sqlite3.connect
في حال عدم وجود ملف بنفس الاسم أصلًا في الحاسوب.
يمكن التأكد من أنّ الكائن connection
قد أُنشئ بنجاح عبر تشغيل الأمر التالي:
print(connection.total_changes)
وبتشغيل شيفرة بايثون السابقة، سيظهر لنا الخرج التالي:
0
إذ تمثّل سمة الكائن connection.total_changes
إجمالي عدد السجلات (الأسطر) التي جرى تغييرها من قبل الكائن connection
، ولكننا لم ننفّذ أي تعليمات SQL حتى الآن، ما يعني أنّ العدد 0 صحيح لعدد التغييرات الإجمالي total_changes
حاليًا.
وفي حال رغبتك بإعادة خطوات هذا المقال من البداية في أي وقت، يمكنك حذف الملف "aquarium.db" من حاسوبك.
ملاحظة: من الممكن أيضًا الاتصال بقاعدة بيانات SQLite مُتصلة بالذاكرة مُباشرةً (وليس بملف) عبر تمرير السلسة النصية الخاصّة ":memory:"
إلى الدالة ()sqlite3.connect
كما يلي:
sqlite3.connect(":memory:")
من الجدير بالملاحظة أنّ هذا النوع من قواعد البيانات سيختفي بمجرّد إنهاء برنامج بايثون، ما يجعلها مناسبة للحالات التي نحتاج فيها إلى استخدام وضع الحماية مؤقتًا (البيئة التي يمكن للمطور من خلالها العمل على مشروع قاعدة بيانات دون تعريض البيانات للخطر في حالة حدوث خطأ ما) بغية اختبار أمر ما في SQLite دون الحاجة إلى البيانات بعد إنهاء البرنامج.
الخطوة الثانية – إضافة بيانات إلى قاعدة البيانات SQLite
الآن وبعد أن أنشأنا الاتصال مع قاعدة بيانات SQLite المُتمثلّة بالملف "aquarium.db"، أصبح من الممكن البدء بإدخال البيانات إلى قاعدة البيانات وقراءتها منها؛ إذ تُخزّن البيانات في قواعد بيانات SQLite ضمن جداول، التي تعرّف مجموعةً من الأعمدة قد تكون خالية تمامًا، أو محتوية على سجل واحد أو أكثر، بحيث يتضمّن كل سجل بيانات موافقة للأعمدة المُعرفّة في الجدول.
سننشئ جدولًا باسم "fish" يتتبع البيانات التالية:
------------------------------------------------------ | tank_number | species | name | ------------------------------------------------------ | 1 | shark | Sammy | ------------------------------------------------------ | 7 | cuttlefish | Jamie | ------------------------------------------------------
سيتتبع الجدول "fish" قيم الاسم name
والنوع species
ورقم الخزّان tank_number
لكل سمكة في الحوض، وقد ضمّنا مثالين لسجلات الأسماك، الأول لسمكة من نوع قرش shark
باسم Sammy
، والآخر لسمكة من النوع حبّار cuttlefish
باسم Jamie
.
ومن الممكن إنشاء الجدول fish
في SQLite اعتمادًا على الكائن connection
المُنشأ في الخطوة الأولى من هذا المقال، على النحو التالي:
cursor = connection.cursor() cursor.execute("CREATE TABLE fish (name TEXT, species TEXT, tank_number INTEGER)")
إذ تعيد الدالة ()connection.cursor
في الشيفرة السابقة كائن مؤشّر Cursor
، والذي يمكنّنا من تنفيذ تعليمات SQL على قاعدة البيانات SQLite باستخدام الدالة ()cursor.execute
، أمّا السلسة النصية "... CREATE TABLE fish"
فهي تعليمة SQL تُنشئ جدولًا باسم fish
مُتضمنًا الأعمدة الثلاث التي أشرنا إليها سابقًا وهي الاسم name
، الذي يحتوي بيانات من النوع النصي TEXT
، والنوع species
الذي يحتوي أيضًا بيانات من النوع النصي TEXT
، ورقم الخزان tank_number
، الذي يحتوي بيانات من نوع عدد صحيح INTEGER
.
الآن وبعدما أنشأنا الجدول، أصبح من الممكن إدخال سجلات البيانات إليه:
cursor.execute("INSERT INTO fish VALUES ('Sammy', 'shark', 1)") cursor.execute("INSERT INTO fish VALUES ('Jamie', 'cuttlefish', 7)")
استدعينا في الشيفرة السابقة الدالة ()cursor.execute
مرتين، المرة الأولى بغية إدخال السجل الخاص بالسمكة القرش المسماة Sammy
إلى الخزان رقم "1"، والثانية لإدخال سمكة الحبار المسمّاة Jamie
إلى الخزان رقم "7"، أمّا السلسة النصية "... INSERT INTO fish VALUES"
فهي تعليمة SQL مسؤولة عن إدخال السجلات إلى الجدول.
أمّا في الخطوة التالية فسنستخدم تعليمة SELECT
من تعليمات SQL بغية التحقّق من السجلات المُدخلة إلى جدول الأسماك "fish".
الخطوة الثالثة – قراءة بيانات من قاعدة البيانات SQLite
أضفنا في الخطوة السابقة سجلين إلى جدول الأسماك "fish" في قاعدة البيانات SQLite، ومن الممكن جلب هذه السجلات باستخدام التعليمة SELECT
من تعليمات SQL على النحو التالي:
rows = cursor.execute("SELECT name, species, tank_number FROM fish").fetchall() print(rows)
وحال تشغيل هذه الشيفرة، سيظهر الخرج التالي:
[('Sammy', 'shark', 1), ('Jamie', 'cuttlefish', 7)]
شغّلت الدالة ()cursor.execute
-في الشيفرة السابقة- تعليمة SELECT
بغية جلب قيم كل من أعمدة الاسم والنوع ورقم الخزان من جدول الأسماك "fish"، لتجلب الدالة ()fetchall
كافّة نتائج التعليمة SELECT
، ولدى تنفيذ التعليمة (print(rows
ستظهر قائمةٌ مكونةٌ من سجلين، ولكل سجل ثلاثة مُدخلات يمثّل كل منها عمود من الأعمدة التي اخترناها من جدول الأسماك، إذ يتضمّن هذان السجلان البيانات التي أدخلناها في الخطوة الثانية، بمعنى أنّنا سنحصل على سجل لسمكة القرش "Sammy"، وسجل لسمكة الحبّار "Jamie"، وفي حال كان المطلوب جلب السجلات من جدول الأسماك التي تحقّق مجموعة من المعايير المُحدّدة، فمن الممكن استخدام العبارة WHERE
على النحو التالي:
target_fish_name = "Jamie" rows = cursor.execute( "SELECT name, species, tank_number FROM fish WHERE name = ?", (target_fish_name,), ).fetchall() print(rows)
وعند تشغيل الشيفرة، سنحصل على الخرج التالي:
[('Jamie', 'cuttlefish', 7)]
تعمل التعليمة ()cursor.execute(<SQL statement>).fetchall
في المثال السابق على جلب كافّة النتائج من التعليمة SELECT
، بينما تعمل العبارة WHERE
في التعليمة SELECT
على ترشيح السجلات لتكون فقط تلك التي تكون فيها قيمة العمود name
هي target_fish_name
، ومن الجدير بالملاحظة أنّه من الممكن استخدام الموضع المؤقت ?
بديلًا عن المتغير target_fish_name
ضمن التعليمة SELECT
، ومن المتوقّع في حالتنا أن يوافق سجلًا واحدًا هذا المعيار، إذ ستكون القيمة المعادة بعد الترشيح هي سجل سمكة الحبّار "Jamie".
تنبيه: لا تستخدم عمليات بايثون على السلاسل النصية أبدًا في إنشاء تعليمات SQL ديناميكيًا، إذ يعرّضك استخدام هذه العمليات في تجميع السلاسل النصية لتعليمات SQL إلى خطر هجمات حقن استعلامات SQL المُستخدمة بغية سرقة أو تحريف أو تعديل البيانات المُخزّنة في قاعدة البيانات، وعوضًا عن ذلك استخدم الموضع المؤقت ?
في تعليمات SQL عند رغبتك بتعويض القيم تلقائيًا من قبل برنامج بايثون، إذ نمرر مجموعةً من القيم المُجمّعة مثل وسيط ثاني في الدالة ()Cursor.execute
لربط هذه القيم بتعليمات SQL، وهذا النمط من التعويض مشروح في مقالنا.
الخطوة 4 – تعديل البيانات في قاعدة بيانات SQLite
من الممكن تعديل السجلات في قاعدة البيانات SQLite باستخدام تعليمتي UPDATE
و DELETE
من تعليمات SQL.
لنفترض على سبيل المثال أنّه قد نُقل القرش "Sammy" إلى الخزان رقم 2، فعندها من الممكن تعديل سجل هذه السمكة في الجدول "fish" للتعبير عن هذا التغيير على النحو التالي:
new_tank_number = 2 moved_fish_name = "Sammy" cursor.execute( "UPDATE fish SET tank_number = ? WHERE name = ?", (new_tank_number, moved_fish_name) )
استخدمنا في الشيفرة السابقة تعليمة UPDATE
من تعليمات SQL لتغيير رقم الخزان tank_number
للسمكة Sammy
إلى القيمة الجديدة "2"، إذ تضمن الجملة WHERE
في التعليمة UPDATE
أنّه لن تتغير قيمة رقم الخزان إلا عند تحقق شرط وهو أن يكون اسم السمكة هو Sammy أي "name = "Sammy
، ومن الممكن التأكّد من تنفيذ التعديل بالشكل الصحيح من خلال تشغيل تعليمة SELECT
التالية:
rows = cursor.execute("SELECT name, species, tank_number FROM fish").fetchall() print(rows)
وبتشغيل الشيفرة السابقة سنحصل على الخرج التالي:
[('Sammy', 'shark', 2), ('Jamie', 'cuttlefish', 7)]
فنلاحظ أنّ السجل الخاص بالسمكة "Sammy" يملك القيمة "2" مثل رقم للخزان ضمن العمود tank_number
.
الآن لنفترض أنّنا حرّرنا القرش Sammy إلى الطبيعة، وبالتالي لم يعد موجودًا في الحوض، عندها يتوجّب حذف السجل الخاص به من الجدول "fish"، لذا سنستخدم التعليمة DELETE
من تعليمات SQL لحذف السجل المطلوب كما يلي:
released_fish_name = "Sammy" cursor.execute( "DELETE FROM fish WHERE name = ?", (released_fish_name,) )
استخدمنا في الشيفرة السابقة التعليمة DELETE
من تعليمات SQL لحذف سجل السمكة Sammy
من النوع shark
، إذ ضمنت الجملة WHERE
في التعليمة DELETE
أنّه لن يُحذف السجل إلّا عند تحقق شرط كون اسم السمكة هو Sammy أي "name = "Sammy
، ومن الممكن التأكّد من تنفيذ الحذف بالشكل الصحيح من خلال تشغيل تعليمة SELECT
التالية:
rows = cursor.execute("SELECT name, species, tank_number FROM fish").fetchall() print(rows)
وبتشغيل الشيفرة السابقة سنحصل على الخرج التالي:
[('Jamie', 'cuttlefish', 7)]
فنلاحظ أنّه قد حُذف فعلًا السجل الخاص بالسمكة Sammy
من النوع shark
، ولم يتبقَ سوى سجل سمكة Jamie
من نوع الحبّار cuttlefish
.
الخطوة 5 – استخدام تعليمة with للإغلاق الآلي
استخدمنا في هذا المقال كائنين رئيسين للتعامل مع قاعدة البيانات "aquarium.db" من النوع SQLite وهما: كائن اتصال باسم connection
وكائن مؤشّر باسم cursor
.
يتوجّب إغلاق ملفات بايثون بعد الانتهاء من العمل عليها، وكذلك الأمر بالنسبة للكائنات مثل Connection
و Cursor
، إذ يجب إغلاقها عند الانتهاء من استخدامها، ومن الممكن استخدام العبارة with
لمساعدتنا على إغلاق الكائنات Connection
و Cursor
تلقائيًا على النحو التالي:
from contextlib import closing with closing(sqlite3.connect("aquarium.db")) as connection: with closing(connection.cursor()) as cursor: rows = cursor.execute("SELECT 1").fetchall() print(rows)
تُعد الدالة closing
من الدوال سهلة الاستخدام التي توفّرها الوحدة contextlib
، فعند إنهاء التعليمة with
، تضمن closing
استدعاء الدالة ()close
بغض النظر عن الكائن المُمرّر إليها، وفي مثالنا استخدمنا الدالة closing
مرتين، الأولى لضمان الإغلاق التلقائي للكائن Connection
المُعاد من الدالة ()sqlite3.connect
، والثانية لضمان الإغلاق التلقائي للكائن Cursor
المُعاد من الدالة ()connection.cursor
، وبتشغيل الشيفرة السابقة سنحصل على الخرج التالي:
[(1,)]
وبما أنّ التعليمة "SELECT 1"
هي تعليمة SQL تعيد دومًا سجلًا وحيدًا بعمود وحيد قيمتة "1"، فمن المنطقي في هذه الحالة الحصول على سجل يحتوي فقط القيمة "1" بمثابة قيمة معادة من الشيفرة.
الخاتمة
تمثّل الوحدة sqlite3
جزءًا فعّالًا من مكتبة بايثون المعيارية، إذ تمكنّنا من العمل مع قاعدة بيانات SQL محلية كاملة الميزات دون الحاجة لتثبيت أي برمجيات إضافية.
استعرضنا في هذا المقال كيفية استخدام الوحدة sqlite3
للاتصال مع قاعدة بيانات SQLite، وكيفية إضافة البيانات إليها وقراءتها منها وتعديلها، كما نوهّنا لأخطار هجمات حقن استعلامات SQL، وبيّنا كيفية استخدام contextlib.closing
لاستدعاء الدالة ()close
تلقائيًا وتطبيقها على كائنات بايثون الموجودة ضمن عبارات with
.
ترجمة -وبتصرف- للمقال How To Use the sqlite3 Module in Python 3 لصاحبه DavidMuller.
اقرأ أيضًا
- إضافة PHP MySQLi ونظام إدارة قواعد البيانات SQLite3
- التعامل مع قواعد البيانات SQLite في تطبيقات Flask
- النسخةالعربية الكاملة لكتاب البرمجة بلغة بايثون
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.