SQLAlchemy je open-source knihovna v Pythonu, která dovoluje dotazování se relačních databází pomocí „SQLAlchemy Core“ a pracovat pomocí objektově relačního mapování (ORM) s databázemi. Knihovna dovoluje vývojáři pracovat s databázemi jako plnohodnotnými objekty v Pythonu.
Popis knihovny
SQLAlchemy poskytuje databázová schémata pro jejich vytvoření, dotazování a objektově relační mapování. Hlavními součástmi knihovny jsou:
- Komplexní zabudovaný doménově specifický jazyk pro SQL v Pythonu s názvem „SQLAlchemy Core“, který poskytuje prostředky pro vytváření a provádění dotazů SQL.
- Výkonný ORM, který umožňuje mapování tříd Pythonu na databázové tabulky.
- Knihovna podporuje migrace datových schémat.
- Knihovna je kompatibilní s různými databázovými backendy.
- Součástí knihovny jsou nástroje databázové propojení i transakční management.
Historie knihovny
K první vydání knihovny došlo v únoru 2006. Vyvinul se tak, že nyní zahrnuje širokou škálu funkcí pro interakci s databázemi a získal si popularitu mezi vývojáři jazyka Python. Mezi významné verze patří:
- Verze 0.1 (2006): Inicializační verze
- Verze 1.0 (2015): Významná vylepšení v ORM, a jazyce SQL výrazů
- Verze 1.4 (2021): Zavedení nového rozhraní ORM API
Příklad využití knihovny v Pythonu
Následující příklad představuje n-k-1 vztah mezi filmy a jejich režiséry. Ukazuje se, jak uživatelsky definované třídy Pythonu vytvářejí odpovídající databázové tabulky, jak se instance s vztahy vytvářejí z obou stran vztahu a nakonec jak lze data dotazovat – ilustruje automaticky generované dotazy SQL pro lazy i eager loading.
Definice schématu
Vytvoření dvou tříd Pythonu a odpovídajících databázových tabulek v DBMS:
#import potřebných prvků z knihovny SQLAlchemy from sqlalchemy import * from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relation, sessionmaker Base = declarative_base() class Movie(Base): __tablename__ = "movies" id = Column(Integer, primary_key=True) # primární klíč pro databázi Movie title = Column(String(255), nullable=False) year = Column(Integer) directed_by = Column(Integer, ForeignKey("directors.id")) # cizí klíč, který propojuje datábazi Movie s databází Director director = relation("Director", backref="movies", lazy=False) def __init__(self, title=None, year=None): self.title = title self.year = year def __repr__(self): return f"Movie({self.title}, {self.year}, {self.director})" class Director(Base): __tablename__ = "directors" id = Column(Integer, primary_key=True) #ID režiséra, které slouží jako primární klíč pro databází Director a jako prvek, který propojuje obě databáze name = Column(String(50), nullable=False, unique=True) def __init__(self, name=None): self.name = name def __repr__(self): return f"Director({self.name})" engine = create_engine("dbms://user:pwd@host/dbname") Base.metadata.create_all(engine)
Vložení dat do databáze pomocí knihovny
Existuje více způsobu vložený jednotlivých záznamů do databází pomocí knihovny:
Session = sessionmaker(bind=engine) session = Session() # vložení jednotlivých objektů do databází m1 = Movie("Robocop", 1987) m1.director = Director("Paul Verhoeven") # vložení objektů jako kolekce d2 = Director("George Lucas") # vložení nového režiséra d2.movies = [Movie("Star Wars", 1977), Movie("THX 1138", 1971)] # vložení dvou filmů, které se přiřadí k Lucasovi try: session.add(m1) session.add(d2) session.commit() except: session.rollback() # navrací provedené změny, aby byla zajištěna integrita dat
Dotazování
# Načítání všech záznamů typu Movie z databáze alldata = session.query(Movie).all() # Procházení všech načtených záznamů for somedata in alldata: # Tisk každého záznamu z databáze v konzoli print(somedata)
SQLAlchemy odešle následující dotaz do DBMS (bez aliasů):
-- Načítá ID, název, rok a ID režiséra z tabulky Movies bez spojení s tabulkou Directors SELECT movies.id, movies.title, movies.year, movies.directed_by, directors.id, directors.name FROM movies LEFT OUTER JOIN directors ON directors.id = movies.directed_by
Výstupem je:
Movie('Robocop', 1987L, Director('Paul Verhoeven')) Movie('Star Wars', 1977L, Director('George Lucas')) Movie('THX 1138', 1971L, Director('George Lucas'))
Nastavením hodnoty lazy=True (default) by SQLAlchemy nejprve vydalo dotaz na získání seznamu filmů a teprve v případě potřeby (lazy) by pro každého režiséra vydalo dotaz na získání jména příslušného režiséra:
-- Načítá ID a jméno z tabulky Directors, ale vyhledává pouze režiséra podle specifického ID (ukázaného jako %s) SELECT movies.id, movies.title, movies.year, movies.directed_by FROM movies -- Získává informace pouze o jednom konkrétním režisérovi SELECT directors.id, directors.name FROM directors WHERE directors.id = %s
