צעדים ראשונים עם SQL Alchemy
פוסט זה כולל טיפ קצר לעבודה יעילה עם Python. אם אתם רוצים ללמוד פייתון יותר לעומק אני ממליץ על קורס Python כאן באתר.
הקורס כולל עשרות שיעורי וידאו והמון תרגול מעשי וילמד אתכם Python בצורה מקצועית מההתחלה ועד הנושאים המתקדמים.
ספריית SQL Alchemy היא אחת הספריות הפופולריות בפייתון לעבודה עם בסיסי נתונים. היא אמינה ובעלת תיעוד מעולה ואפילו תומכת בעבודה עם asyncio. בפוסט זה ניקח שלושה צעדים ראשונים כדי שיהיה לכם קל להתחיל לעבוד עם הספריה ואשתדל לכסות כמה שיותר מושגים בסיסיים שלה.
1. התקנה וחיבור לבסיס הנתונים
צעד ראשון בעבודה עם SQL Alchemy הוא להתקין את הספריה. בפייתון זה פשוט אומר להפעיל:
$ pip install sqlalchemy
שווה לציין שלי זה לא הצליח מהפעם הראשונה בגלל שידרוגי גירסאות. אם אתם מקבלים הודעות שגיאה מוזרות שווה להפעיל שידרוג לגירסה החדשה ביותר של pip ושל setuptools עם:
pip install -U pip setuptools
ואחרי זה הכל יתחיל לעבוד. כדי לוודא שאתם מותקנים אפשר להיכנס למסוף פייתון ושם לכתוב:
Python 3.10.0 (default, Nov 3 2021, 23:29:09) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlalchemy
>>> print(sqlalchemy.__version__)
1.4.36
>>>
כל העבודה מול בסיס הנתונים ב SQL Alchemy מבוצעת דרך אוביקט שנקרא Engine. הדבר המרכזי שנעשה איתו זה ליצור חיבור, ולכן כשיוצרים את ה Engine צריך להגיד לו לאיזה בסיס נתונים אנחנו רוצים להתחבר. אני מפעיל את הדוגמה עם SQLite שנשמר בקובץ ולכן מפעיל:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, insert, select, bindparam
engine = create_engine("sqlite+pysqlite:///db.sql", echo=True, future=True)
שם הקובץ יהיה db.sql
וזה נוח לשמור בסיס נתונים שלם בקובץ כשמשחקים עם ספריה חדשה כי לא צריך להתקין כלום וקל להסתכל בקובץ (באמצעות SQLite CLI או כל כלי גרפי אחר) ולראות שדברים נוצרו כמו שחשבנו שהם צריכים להיות.
2. יצירת טבלאות
פעולה ראשונה שנרצה לעשות בעבודה עם בסיס נתונים היא לאתחל אותו, כלומר ליצור את הטבלאות אם הן לא קיימות. ברוב המקרים לא באמת נצטרך ליצור טבלאות כי בסיס הנתונים כבר יהיה קיים, אבל אנחנו רק משחקים עם הספריה.
בשביל ליצור את הטבלאות אנחנו צריכים קודם להגדיר מה צריך להיות בהן, כלומר מה העמודות בכל טבלה ומה היחסים בין הטבלאות. ספריית SQL Alchemy מספקת את המחלקות Table ו Column כדי להגדיר טבלאות ועוד כמה מחלקות לסוגי נתונים. בואו ניקח דוגמה ונגדיר שתי טבלאות אחת עבור ספרים והשניה עבור סופרים, ונקבל:
metadata_obj = MetaData()
authors_table = Table(
"authors",
metadata_obj,
Column('id', Integer, primary_key=True),
Column('name', String()))
books_table = Table(
"books",
metadata_obj,
Column('ISBN', String(13), primary_key=True),
Column('name', String()),
Column('author_id', ForeignKey('authors.id'), nullable=False))
האוביקט metadata מרכז את כל נתוני הטבלאות, אוביקט Table מייצג טבלה ו Column מייצג עמודה. ביצירה של עמודות אפשר להעביר את סוגי הנתונים ומאפיינים של העמודה כמו primary_key
או nullable
.
אחר כך מגיעה השורה האופציונאלית:
metadata_obj.create_all(engine)
שיוצרת את כל הטבלאות. עליה נוכל לוותר כשנעבוד מול בסיס נתונים קיים.
3. הכנסת מידע
בעבודה עם SQL Alchemy יש לי ביד מנוע (engine) ובעזרתו אני מתחבר לבסיס הנתונים. החיבור תמיד כולל טרנזאקציה, מבוצע בתוך בלוק with
ובסוף הבלוק, אם הכל עבד כמו שצריך, יהיה commit
. אני משתמש בפקודה engine.begin
כדי להתחיל את החיבור ולבצע אוטומטית את ה commit בסוף הבלוק.
בתוך הבלוק שיטת העבודה היא לפי תבנית שנקראת Command Pattern. זה אומר שיש פונקציות של SQL Alchemy שמייצגות פקודות שיכולות להישלח לבסיס הנתונים, ואוביקט החיבור יודע להפעיל אותן עם הפונקציה execute
שלו. אז בשביל להפעיל הכנסה לטבלת המחברים אני אפעיל:
# Part 2 - Insert some data
with engine.begin() as conn:
conn.execute(insert(authors_table), [
{ "name": "Jay Shetty" },
{ "name": "David Foster Wallace" },
{ "name": "Marcus Aurelius" },
])
הפונקציה insert
יוצרת פקודת הכנסה, והפונקציה conn.execute
מקבלת את פקודת ההכנסה ואוסף של פרמטרים לפקודה ומפעילה אותה כדי להכניס את שלושת השורות.
הכנסה יותר מורכבת תהיה לטבלת הספרים: שם אני צריך להשתמש בתת-שאילתה כדי לדעת מה ה id
של הסופר שכתב את הספר. אני לא יודע את ה id כי בסיס הנתונים יצר אותו אוטומטית בעת ההכנסה ולכן צריך לתשאל.
אני יוצר אוביקט שאילתה, שגם הוא פקודה, עם הפקודה:
author_subquery = (
select(authors_table.c.id).
where(authors_table.c.name == bindparam('author_name')).
scalar_subquery())
ומשלב אותו בפקודת ההכנסה באופן הבא:
conn.execute(insert(books_table).values(author_id=author_subquery), [
{ "ISBN": "9781982134488", "name": "Think Like a Monk", "author_name": "Jay Shetty" },
{ "ISBN": "9780316066525", "name": "Infinite Jest", "author_name": "David Foster Wallace" },
{ "ISBN": "9781515208563", "name": "Meditations", "author_name": "Marcus Aurelius" },
])
4. שליפת מידע ו Join
ואם כבר ראינו את select
בואו נרחיב עליו ונכתוב שאילתה שמושכת את כל הספרים והמחברים שלהם. הפקודה select
מקבלת רשימה של עמודות ומחזירה "פקודת" שליפה. הפעלת פקודות כמו where
על פקודת השליפה מוסיפה מידע לשליפה עד שיש לנו פקודה שלמה ואותה מעבירים ל execute.
כדי לקבל את כל הספרים של כל המחברים אני רוצה להפעיל פקודת שליפה עם join
והקוד נראה כך:
with engine.begin() as conn:
# Part 3 - Join Select
books = conn.execute(select(books_table.c.name, books_table.c.ISBN, authors_table.c.name).join_from(books_table, authors_table))
for book in books:
book_name, isbn, author = book
print(f"Book: {book_name}; ISBN: {isbn}; author: {author}")
הפקודה select
לוקחת רשימה של עמודות, עליה אני מפעיל את הפקודה join_from
כדי להגיד לה מה סדר ה Join-ים והיא כבר מחשבת את פקודת ה SQL אוטומטית.
5. הקוד המלא ולאן ממשיכים
סך הכל הקוד שראינו בכל הפוסט הוא:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey, insert, select, bindparam
# Part 1 - Create the tables
engine = create_engine("sqlite+pysqlite:///db.sql", echo=True, future=True)
metadata_obj = MetaData()
authors_table = Table(
"authors",
metadata_obj,
Column('id', Integer, primary_key=True),
Column('name', String()))
books_table = Table(
"books",
metadata_obj,
Column('ISBN', String(13), primary_key=True),
Column('name', String()),
Column('author_id', ForeignKey('authors.id'), nullable=False))
metadata_obj.create_all(engine)
# Part 2 - Insert some data
with engine.begin() as conn:
conn.execute(insert(authors_table), [
{ "name": "Jay Shetty" },
{ "name": "David Foster Wallace" },
{ "name": "Marcus Aurelius" },
])
author_subquery = (
select(authors_table.c.id).
where(authors_table.c.name == bindparam('author_name')).
scalar_subquery())
conn.execute(insert(books_table).values(author_id=author_subquery), [
{ "ISBN": "9781982134488", "name": "Think Like a Monk", "author_name": "Jay Shetty" },
{ "ISBN": "9780316066525", "name": "Infinite Jest", "author_name": "David Foster Wallace" },
{ "ISBN": "9781515208563", "name": "Meditations", "author_name": "Marcus Aurelius" },
])
with engine.begin() as conn:
# Part 3 - Join Select
books = conn.execute(select(books_table.c.name, books_table.c.ISBN, authors_table.c.name).join_from(books_table, authors_table))
for book in books:
book_name, isbn, author = book
print(f"Book: {book_name}; ISBN: {isbn}; author: {author}")
אני מקווה שהוא עזר לכם לקבל תמונה מגבוה על SQL Alchemy. אם אהבתם את הממשק ותרצו ללמוד עוד על הספריה אני ממליץ על התיעוד שלהם ובמיוחד על ה Tutorial, שהרבה מהדוגמאות בפוסט מבוססות עליו. זה הקישור: