ubuntuusers.de

Hinweis: Dies ist ein statischer Snapshot unseres Wikis vom 25. März 2013 und kann daher nicht bearbeitet werden. Der aktuelle Artikel ist unter wiki.ubuntuusers.de zu finden.

SQLAlchemy

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

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

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

SQLAlchemy legt sich dabei als Zwischenschicht 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. So nutzt z.B. auch die von ubuntuusers.de verwendete Portal-Software Inyoka SQLAlchemy für die Datenbankanbindung.

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 über

  • python-sqlalchemy (universe)

Wiki/Vorlagen/Installbutton/button.png mit apturl

Paketliste zum Kopieren:

sudo apt-get install python-sqlalchemy 

sudo aptitude install python-sqlalchemy 

installiert werden[1].

Optional kann man auch die jeweils passende Dokumentation mit installieren:

  • python-sqlalchemy-doc (universe)

Wiki/Vorlagen/Installbutton/button.png mit apturl

Paketliste zum Kopieren:

sudo apt-get install python-sqlalchemy-doc 

sudo aptitude install python-sqlalchemy-doc 

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 easy_install 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 seit Python 2.5. - und damit bei allen unterstützen Ubuntu-Versionen - in Python selbst enthalten, so dass hier keine weitere Installation notwendig ist.

Ansonsten muss man eines oder mehrere der folgenden Pakete nachinstallieren:

  • für MySQL benötigt man das Paket python-mysqldb.

  • für PostgreSQL benötigt man eines der folgenden Pakete: python-psycopg2

  • für MS SQLServer und Sybase: python-pymssql (universe)

Weitere mögliche Treiber, welche vielleicht nicht in den Ubuntu-Quellen zu finden sind, listet die Seite Supported DB-APIs {en} 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

python 

im Terminal öffnet[2].

Engine

Die Engine {en}, 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('mysql://otto:normal@localhost/test')

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 {en}.

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
>>> from sqllchemy 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()
2011-01-31 19:40:54,960 INFO sqlalchemy.engine.base.Engine.0x...8a8c PRAGMA table_info("programme")
2011-01-31 19:40:54,960 INFO sqlalchemy.engine.base.Engine.0x...8a8c ()
2011-01-31 19:40:54,961 INFO sqlalchemy.engine.base.Engine.0x...8a8c 
CREATE TABLE programme (
	id INTEGER NOT NULL, 
	name VARCHAR(20), 
	desktop VARCHAR(10), 
	PRIMARY KEY (id)
)
2011-01-31 19:40:54,962 INFO sqlalchemy.engine.base.Engine.0x...8a8c ()
2011-01-31 19:40:54,978 INFO sqlalchemy.engine.base.Engine.0x...8a8c COMMIT

Wie man sieht gibt SQLAlchemy durch das 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 {en} 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')
2011-01-31 20:11:24,238 INFO sqlalchemy.engine.base.Engine.0x...8a8c INSERT INTO programme (name, desktop) VALUES (?, ?)
2011-01-31 20:11:24,238 INFO sqlalchemy.engine.base.Engine.0x...8a8c ('Gedit', 'GNOME')
2011-01-31 20:11:24,239 INFO sqlalchemy.engine.base.Engine.0x...8a8c COMMIT
>>> engine.execute(tab.insert(), name=Kate, desktop='KDE')
2011-01-31 20:11:38,700 INFO sqlalchemy.engine.base.Engine.0x...8a8c INSERT INTO programme (name, desktop) VALUES (?, ?)
2011-01-31 20:11:38,700 INFO sqlalchemy.engine.base.Engine.0x...8a8c ('Kate', 'KDE')
2011-01-31 20:11:38,702 INFO sqlalchemy.engine.base.Engine.0x...8a8c COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x88e0d0c>
>>> conn = engine.connect()
>>> conn.execute(tab.insert(), [
... {'name': 'Evolution', 'desktop': 'GNOME'},
... {'name': 'KOffice', 'desktop': 'KDE'},
... {'name': 'Thunar', 'desktop': 'Xfce'}])
2011-01-31 20:18:07,228 INFO sqlalchemy.engine.base.Engine.0x...8a8c INSERT INTO programme (name, desktop) VALUES (?, ?)
2011-01-31 20:18:07,229 INFO sqlalchemy.engine.base.Engine.0x...8a8c (('Evolution', 'GNOME'), ('KOffice', 'KDE'), ('Thunar', 'Xfce'))
2011-01-31 20:18:07,230 INFO sqlalchemy.engine.base.Engine.0x...8a8c COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x9989a4c>
>>> 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)
2011-01-31 20:42:24,304 INFO sqlalchemy.engine.base.Engine.0x...8a6c SELECT programme.name, programme.desktop 
FROM programme
2011-01-31 20:42:24,305 INFO sqlalchemy.engine.base.Engine.0x...8a6c ()
>>> 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()
2011-01-31 20:45:44,692 INFO sqlalchemy.engine.base.Engine.0x...8a6c SELECT programme.name, programme.desktop 
FROM programme 
WHERE programme.desktop = ?
2011-01-31 20:45:44,693 INFO sqlalchemy.engine.base.Engine.0x...8a6c ('GNOME',)
[(u'Gedit', u'GNOME'), (u'Evolution', u'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 {en} 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 {en}

  2. dann wird eine sogenannte Session gestartet {en}

  3. nun können Abfrage 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 0x88e0dcc; 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()
2011-01-31 21:12:24,481 INFO sqlalchemy.engine.base.Engine.0x...8a6c BEGIN
2011-01-31 21:12:24,483 INFO sqlalchemy.engine.base.Engine.0x...8a6c INSERT INTO programme (name, desktop) VALUES (?, ?)
2011-01-31 21:12:24,484 INFO sqlalchemy.engine.base.Engine.0x...8a6c ('Totem', 'GNOME')
2011-01-31 21:12:24,488 INFO sqlalchemy.engine.base.Engine.0x...8a6c COMMIT
>>> neues_programm.id
2011-01-31 21:12:37,969 INFO sqlalchemy.engine.base.Engine.0x...8a6c BEGIN
2011-01-31 21:12:37,970 INFO sqlalchemy.engine.base.Engine.0x...8a6c SELECT programme.id AS programme_id, programme.name AS programme_name, programme.desktop AS programme_desktop 
FROM programme 
WHERE programme.id = ?
2011-01-31 21:12:37,971 INFO sqlalchemy.engine.base.Engine.0x...8a6c (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
... 
2011-01-31 21:19:00,944 INFO sqlalchemy.engine.base.Engine.0x...8a6c SELECT programme.name AS programme_name 
FROM programme 
WHERE programme.desktop = ?
2011-01-31 21:19:00,944 INFO sqlalchemy.engine.base.Engine.0x...8a6c ('GNOME',)
(u'Gedit',)
(u'Evolution',)
(u'Totem',)

Weitere Informationen zu Abfragen via ORM {en} 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 Dokumentaion {en} empfohlen, welche zwei Tutorials, viele Detailinformationen und Beispiele sowie eine umfassende API-Referenz bietet.

SqlSoup

Möchte man nachträglich ein ORM zusammen mit einer bestehenden Datenbank verwenden, so kann oder möchte man wahrscheinlich nicht mehr das gegebene Datenbankschema ändern. Natürlich ist die Verwendung von SQLAlchemy trotzdem ohne weiteres möglich, wenn man alle Tabellen „von Hand“ gegen die Datenbank mappt. Dies kann aber recht mühselig sein, besonders dann, wenn die Datenbank viele verschiedene Tabellen enthält.

An dieser Stelle kommt SqlSoup ins Spiel. Das Modul ist so etwas wie ein „Mapper im Vollautomatik-Modus“. Dies bedeutet, dass man mit einem einzelnen Befehl die komplette Mapping der Datenbank auf Python-Objekte erfolgt. Die Objekte werden dabei ebenfalls von SqlSoup angelegt. Im direkten Vergleich zum regulären Mapper bietet SqlSoup weniger Möglichkeiten, eben weil ein Großteil der Konfiguration automatisch läuft. Für viele Anwendungsfälle sollte es aber nichts desto trotz ausreichend sein.

SqlSoup ist zwar kein direkter Bestandteil von SQLAlchemy, ist aber trotzdem Paket enthalten, d.h. es wird immer mit SQLAlchemy zusammen installiert.

Im folgenden, kurzen Beispiel wird die weiter oben angelegte Datenbank "uude.db" mit Hilfe von SqlSoup automatisch auf Python Objekte gemappt, ein Abfrage gemacht, ein weiterer Eintrag hinzugefügt und zum Schluss werden alle Programme für den KDE Desktop angezeigt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
>>> from sqlalchemy.ext.sqlsoup import SqlSoup
>>> db = SqlSoup('sqlite:///uude.db')
>>> db.Programme.all()
[MappedProgramme(id=1,name=u'Gedit',desktop=u'GNOME'), MappedProgramme(id=2,name=u'Kate',desktop=u'KDE'), MappedProgramme(id=3,name=u'Evolution',desktop=u'GNOME'), MappedProgramme(id=4,name=u'KOffice',desktop=u'KDE'), MappedProgramme(id=5,name=u'Thunar',desktop=u'Xfce'), MappedProgramme(id=6,name=u'Totem',desktop=u'GNOME')]
>>> db.Programme.insert(name='Amarok',desktop='KDE')
MappedProgramme(id=None,name='Amarok',desktop='KDE')
>>> db.flush()
>>> db.Programme.all()
[MappedProgramme(id=1,name=u'Gedit',desktop=u'GNOME'), MappedProgramme(id=2,name=u'Kate',desktop=u'KDE'), MappedProgramme(id=3,name=u'Evolution',desktop=u'GNOME'), MappedProgramme(id=4,name=u'KOffice',desktop=u'KDE'), MappedProgramme(id=5,name=u'Thunar',desktop=u'Xfce'), MappedProgramme(id=6,name=u'Totem',desktop=u'GNOME'), MappedProgramme(id=7,name='Amarok',desktop='KDE')]
>>> for prog in db.Programme.filter(db.Programme.desktop=='KDE'):
...     print prog.id, prog.name
... 
2 Kate
4 KOffice
7 Amarok

Eine vollständige Übersicht über das Modul findet man in der SqlSoup Dokumentation {en}.

Alternativen:

  • Storm {en} - ein ORM, welches von Canonical entwickelt wird

ubuntuusers.local › WikiSQLAlchemy