SQLAlchemy

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Dieser Artikel ist mit keiner aktuell unterstützten Ubuntu-Version getestet! Bitte teste diesen Artikel für eine Ubuntu-Version, welche aktuell unterstützt wird. Dazu sind die Hinweise zum Testen von Artikeln zu beachten.

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

  1. ⚓︎ Installation von Programmen

  2. ⚓︎ Einen Terminal öffnen

Inhaltsverzeichnis
  1. Installation
    1. Alternative Installation
    2. Datenbankanbindung installieren
  2. Benutzung
    1. Engine
    2. Tabellen anlegen und Metadaten
    3. SQL Expression Language
    4. ORM
    5. Weitere Dokumentation
  3. Links

sa_logo.gif SQLAlchemy 🇬🇧 ist ein Objekt-relationaler Mapper (ORM) und eine SQL-Werkzeugsammlung für Python.

SQLAlchemy legt sich dabei als Schicht zwischen die Anwendung und die relationale Datenbank. Der Vorteil ist dabei, dass sich via SQLAlchemy alle unterstützten Datenbanken einheitlich ansprechen lassen, da die Unterschiede in den SQL-Dialekten durch das Programm „ausgeglichen“ werden. Das ORM-Modul gilt als extrem leistungsfähig und ermöglicht auch komplexe „Mappings“ zwischen einer Datenbank und Python-Objekten.

SQLAlchemy unterstützt eine Reihe von relationalen Datenbanken, wie z.B. SQLite, MySQL und PostgreSQL aus dem OpenSource Lager, aber auch proprietäre Datenbanken wie Orcale und MS SQL Server finden Unterstützung. Da die Anzahl der unterstützten Datenbanken in der Regel mit jeder Hauptversion von SQLAlchemy steigt, sollte man für weitere Details die jeweilige Dokumentation lesen.

Installation

SQLAlchemy ist in den Paketquellen enthalten und kann für Python 3 über

Befehl zum Installieren der Pakete:

sudo apt-get install python3-sqlalchemy 

Oder mit apturl installieren, Link: apt://python3-sqlalchemy

installiert werden[1].

Alternative Installation

Da die Version aus den Paketquellen nicht unbedingt die neuste ist bzw. nicht unbedingt die neuste „Unterversion“ ist, so dass unter Umständen Fehlerkorrekturen fehlen, kann man SQLAlchemy alternativ auch über pip installieren.

Datenbankanbindung installieren

Egal ob man SQLAlchemy über die Paketquellen oder manuell installiert – die entsprechenden Python-Module für die diversen Datenbanken müssen immer nachträglich installiert werden.

Die Anbindung an SQLite ist in Python selbst enthalten, so dass hier keine weitere Installation notwendig ist.

Ansonsten muss man eines oder mehrere der folgenden Pakete nachinstallieren:

Weitere mögliche Treiber, welche vielleicht nicht in den Ubuntu-Quellen zu finden sind, listet die Seite Supported DB-APIs 🇬🇧 auf.

Benutzung

Wie in der Einleitung bereits erwähnt besteht SQLAlchemy aus mehreren Komponenten, die unabhängig von einander genutzt werden können. Man kann also z.B. auch „nur“ eine „Engine“ oder die „SQL Expression Language“ benutzen, ohne den kompletten ORM importieren zu müssen.

Alle folgenden Befehle können direkt in der interaktiven Python-Konsole ausgeführt werden, welche man durch den Aufruf von

python3 

im Terminal öffnet[2].

Engine

Die Engine 🇬🇧, also die Anbindung an die Datenbank an sich, stellt mit die unterste Ebene der Benutzerschnittstelle von SQLAlchemy dar. Der Vorteil gegenüber einer „gewöhnlichen“ Verbindung ist, dass die Verbindung über SQLAlchemy je nach Notwendigkeit automatisch auf- und abgebaut wird und direkt auch ein Cursor-ähnliches Objekt zur Datenabfrage bereit stellt. Ebenso verwaltet SQLAlchemy automatisch mehrere Verbindungen zu einer Datenbank über den sogenannten „Connection Pool“.

Die Datenbank wird immer gemäß einem URL-ähnlichen Schema angegeben, das wie folgt aussieht:

DATENBANK://NUTZER:PASSWORD@SERVER:PORT/DATENBANKNAME

Der Port ist optional und kann weggelassen werden, so lang die Datenbank auf deren Standardport läuft.

Will sich der Benutzer otto mit dem Passwort normal also z.B. mit einem lokalen MySQL Datenbankserver und der Datenbank Namens test verbinden, so sieht dies so aus:

mysql://otto:normal@localhost/test

Eine kleine Abweichung gibt es bei SQLite, da SQLite keine Benutzerverwaltung in dieser Form kennt und die Datenbank immer als lokale Datei vorliegt:

sqlite:///meine_db.db             # relativer Pfad
sqlite:////pfad/zu/meine_db.db    # absoluter Pfad
sqlite://                         # keine Angaben = In-Memory Datenbank

Die Anbindung wird via SQLAlchemy so erzeugt:

1
2
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///test.db')

Natürlich kennt create_engine eine Vielzahl von Optionen und Parametern. Erwähnt werden soll hier aber nur echo=True. Damit gibt sich SQLAlchemy besonders geschwätzig und gibt z.B. alle generierten SQL-Befehle auf Stdout, also in der Regel der Konsole, aus.

Nun kann über die Execute-Funktion ein SQL-Query abgesetzt werden, z.B.:

1
2
3
4
5
>>> ergebnis = engine.execute("SELECT foo FROM bar WHERE spam = 'egg'")
>>> for erg in ergebnis:
...     print(erg)

(Ausgabe der Ergebnisse)

Tabellen anlegen und Metadaten

Egal ob man später das ORM-Modul oder die SQL Expression Language benutzen möchte, das Anlegen von Tabellen erfolgt in beiden Fällen gleich. Dazu speichert das Programm Daten, Schemen etc. zu Tabellen mit Hilfe sogenannter database metadata 🇬🇧.

Im folgenden Beispiel werden die notwendigen Importe durchgeführt, im aktuellen Verzeichnis die SQLite Datenbanknamens uude.de angelegt, die Metadaten an die Engine gebunden und dann eine einfache Tabelle Namens programme angelegt, welche aus drei Spalten besteht:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
>>> engine = create_engine('sqlite:///uude.db', echo=True)
>>> metadata = MetaData()
>>> metadata.bind = engine
>>> tab = Table('programme', metadata,
... Column('id', Integer, primary_key=True),
... Column('name', String(20)),
... Column('desktop', String(10)))
>>> metadata.create_all()
2016-05-22 14:55:26,465 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("programme")
2016-05-22 14:55:26,465 INFO sqlalchemy.engine.base.Engine ()
2016-05-22 14:55:26,466 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE programme (
	id INTEGER NOT NULL, 
	name VARCHAR(20), 
	desktop VARCHAR(10), 
	PRIMARY KEY (id)
)


2016-05-22 14:55:26,466 INFO sqlalchemy.engine.base.Engine ()
2016-05-22 14:55:26,476 INFO sqlalchemy.engine.base.Engine COMMIT

Wie man sieht gibt SQLAlchemy durch den Parameter echo=True die abgesetzten SQL-Befehle in der Konsole aus. Der Befehl metadata.create_all() legt dabei letztendlich die Tabelle an. Per Voreinstellung wird dabei immer zuerst automatisch geprüft, ob die Tabelle existiert, d.h. bestehende Daten werden nie gelöscht.

SQLAlchemy bietet auch die Möglichkeit, eine existierende Tabelle in die Metadaten zu übernehmen. Angenommen, in der gleichen Datenbank gäbe es bereits eine Tabelle distributionen. Dann würde der Befehl

1
>>> tab2 = Table('distributionen', metadata, autoload=True)

diese automatisch tab2 zuordnen.

Nach dem Anlegen von Metadaten bzw. der Zuordnung zu einer Variablen sind die einzelnen Spalten einer Datenbank direkt ansprechbar. Im Falle der Tabelle tab und der Spalte id zum Beispiel so:

1
2
>>> tab.c.id
Column('id', Integer(), table=<programme>, primary_key=True, nullable=False)

Das c ist dabei die Kurzform für „Column“.

SQL Expression Language

Die SQL Expression Language 🇬🇧 ist, vereinfacht ausgedrückt, die „pythonische“ Variante von SQL. Das heißt, es gibt diverse Python-Klassen und Funktionen, die die üblichen SQL-Befehl darstellen. Dies wird im Folgenden anhand einiger kurzer Beispiele gezeigt.

Ausgehend von der weiter oben angelegten Tabelle wird diese mit ein paar Datensätzen befüllt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
>>> ergebnis = engine.execute(tab.insert(), name='Gedit', desktop='GNOME')
2016-05-22 14:57:47,165 INFO sqlalchemy.engine.base.Engine INSERT INTO programme (name, desktop) VALUES (?, ?)
2016-05-22 14:57:47,165 INFO sqlalchemy.engine.base.Engine ('Gedit', 'GNOME')
2016-05-22 14:57:47,165 INFO sqlalchemy.engine.base.Engine COMMIT
>>> engine.execute(tab.insert(), name='Kate', desktop='KDE')
2016-05-22 14:58:12,056 INFO sqlalchemy.engine.base.Engine INSERT INTO programme (name, desktop) VALUES (?, ?)
2016-05-22 14:58:12,056 INFO sqlalchemy.engine.base.Engine ('Kate', 'KDE')
2016-05-22 14:58:12,057 INFO sqlalchemy.engine.base.Engine COMMIT
<sqlalchemy.engine.result.ResultProxy object at 0x7f1212641c90>
>>> conn = engine.connect()
>>> conn.execute(tab.insert(), [
... {'name': 'Evolution', 'desktop': 'GNOME'},
... {'name': 'KOffice', 'desktop': 'KDE'},
... {'name': 'Thunar', 'desktop': 'Xfce'}])
2016-05-22 14:59:13,295 INFO sqlalchemy.engine.base.Engine INSERT INTO programme (name, desktop) VALUES (?, ?)
2016-05-22 14:59:13,295 INFO sqlalchemy.engine.base.Engine (('Evolution', 'GNOME'), ('KOffice', 'KDE'), ('Thunar', 'Xfce'))
2016-05-22 14:59:13,295 INFO sqlalchemy.engine.base.Engine COMMIT
<sqlalchemy.engine.result.ResultProxy object at 0x7f1212641f50>
>>> conn.close()

Wie man sieht, kennt SQLAlchemy sowohl die explizite (mit connection) und implizite (ohne connection) Ausführung von Befehlen. Ebenso kann das Ergebnis, der sogenannte ResultProxy einer Variablen zugeordnet werden, wie im ersten Befehl. Außerdem ist zu sehen, dass SQLAlchemy standardmäßig im „Auto-Commit“ Modus arbeitet, bei dem Befehle direkt in der Datenbank ausgeführt werden. Dieses Verhalten lässt sich natürlich durch das Setzen der entsprechenden Option ändern.

Da die Datenbank nun ein wenig Inhalt erhalten hat, können auch Abfragen durchgeführt werden:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> from sqlalchemy import select
>>> query = select([tab.c.name,tab.c.desktop])
>>> ergebnis = engine.execute(query)
2016-05-22 15:00:07,716 INFO sqlalchemy.engine.base.Engine SELECT programme.name, programme.desktop 
FROM programme
2016-05-22 15:00:07,716 INFO sqlalchemy.engine.base.Engine ()
>>> for erg in ergebnis:
...     print(erg['name'], erg['desktop'])
... 
Gedit GNOME
Kate KDE
Evolution GNOME
KOffice KDE
Thunar Xfce

# nur GNOME Programme wählen:

>>> query = select([tab.c.name,tab.c.desktop])
>>> query = query.where(tab.c.desktop == 'GNOME')
>>> engine.execute(query).fetchall()
2016-05-22 15:01:18,970 INFO sqlalchemy.engine.base.Engine SELECT programme.name, programme.desktop 
FROM programme 
WHERE programme.desktop = ?
2016-05-22 15:01:18,970 INFO sqlalchemy.engine.base.Engine ('GNOME',)
[('Gedit', 'GNOME'), ('Evolution', 'GNOME')]

Die Beispiele stellen natürlich nur einen Bruchteil der Funktionalität und Leistungsfähigkeit der SQL Expression Language dar. Viele weitere Beispiele findet man in der weiter oben verlinkten Dokumenation bzw. in dem dort befindlichen Tutorial.

ORM

Der Objekt-Relationale Mapper von SQLAlchemy ist eines der leistungsfähigsten Module. Mit seiner Hilfe können Tabellen (bzw. Spalten) von relationalen Datenbanken auf ein Python Objekt bzw. eine Python Klasse „gemappt“ (d.h. abgebildet) werden. Eine grundlegenden Einführung 🇬🇧 in den ORM findet man in der Dokumentation.

Das Arbeiten mit dem ORM erfolgt grundsätzlich in drei Schritten:

  1. als erstes wird ein Mapper angelegt 🇬🇧

  2. dann wird eine sogenannte Session gestartet 🇬🇧

  3. nun können Abfragen gemacht werden, Einträge angelegt werden etc.

Im folgenden Beispiel wird ein sehr einfaches Mapping angelegt. Der Mapper von SQLAlchemy ist wesentlich leistungsfähiger und unterstützt unter anderem auch Relationen, Rückwärtsreferenzen, Joins, Vererbung etc.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> class Programme(object):
...     pass
... 
>>> from sqlalchemy.orm import mapper
>>> mapper(Programme, tab)
<Mapper at 0x7f1212641850; Programme>
>>> neues_programm = Programme()
>>> neues_programm.name = 'Totem'
>>> neues_programm.desktop = 'GNOME'
>>> neues_programm.desktop
'GNOME'
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=engine)
>>> session = Session()
>>> session.add(neues_programm)
>>> session.commit()
2016-05-22 15:04:11,216 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-05-22 15:04:11,216 INFO sqlalchemy.engine.base.Engine INSERT INTO programme (name, desktop) VALUES (?, ?)
2016-05-22 15:04:11,216 INFO sqlalchemy.engine.base.Engine ('Totem', 'GNOME')
2016-05-22 15:04:11,217 INFO sqlalchemy.engine.base.Engine COMMIT
>>> neues_programm.id
2016-05-22 15:04:57,923 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-05-22 15:04:57,924 INFO sqlalchemy.engine.base.Engine SELECT programme.id AS programme_id, programme.name AS programme_name, programme.desktop AS programme_desktop 
FROM programme 
WHERE programme.id = ?
2016-05-22 15:04:57,924 INFO sqlalchemy.engine.base.Engine (6,)
6

Wie man sieht, reicht es, für das Mapping eine leere Klasse anzulegen. Aber natürlich kann man auch das sonst notwendige __init__ definieren sowie weitere Funktionen in der Klasse anlegen.

Abfragen sind so auch möglich:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> ergebnis = session.query(Programme.name).filter(Programme.desktop == 'GNOME')
>>> for erg in ergebnis:
...     print(erg)
... 
2016-05-22 15:05:39,091 INFO sqlalchemy.engine.base.Engine SELECT programme.name AS programme_name 
FROM programme 
WHERE programme.desktop = ?
2016-05-22 15:05:39,091 INFO sqlalchemy.engine.base.Engine ('GNOME',)
('Gedit',)
('Evolution',)
('Totem',)

Weitere Informationen zu Abfragen via ORM 🇬🇧 findet man in der Dokumentation.

Weitere Dokumentation

Alle obigen Beispiele zeigen nur einige wenige Grundlagen von SQLAlchemy. Wer sich näher mit SQLAlchemy beschäftigen möchte, dem sei die sehr gute und ausführliche, offizielle Dokumentation 🇬🇧 empfohlen, welche zwei Tutorials, viele Detailinformationen und Beispiele sowie eine umfassende API-Referenz bietet.