Sonntag, 25. August 2013

sostore - eine simple Objekt-Datenbank für Python

Der Python Cheeseshop ist schon ein echte Fundgrube - zumindest mir geht es so, dass man da was sucht und dann zufällig auf was ganz anderes stößt, was man zwar eigentlich nicht braucht, aber trotzdem interessant ist.

Neulich bin ich so auf sostore gestoßen. Der Name steht für "SQLite Object Store" und das Projekt beschreibt sich als "An absurdly simple object "database" for Python".

Der Name ist vielleicht etwas irreführend weil: sostore speichert nicht wirklich Objekte (im Sinne von Python-Objekten), sondern "nur" Python Dictionaries.  Gespeichert wird dabei in eine SQLite Datenbank. Das Modul besteht nur aus zwei Dateien und hat keine Abhängigkeiten außer Python selber.

Dieser Blogeintrag beschreibt die Nutzung der zur Zeit aktuellste, stabilen Version 0.3.
(Nachtrag 17.9.2013: es gibt inzwischen die Version 0.4. Ein Update zu diesem Blogpost gibt es hier: Link)

Wie andere Python-Module auch lässt sich sostore via pip installieren:

$ pip install sostore

Danach steht das Modul unter dem Namen sostore zur Verfügung:

>>> import sostore

Die einzige Klasse, mit der man als Nutzer interagiert, heißt Collection. Diese enthält alle Daten und stellt alle Funktionen bereit.

Als erstes wird eine neue Collection angelegt:

>>> c = sostore.Collection('people')

Per Voreinstellung wird die im Hintergrund laufende SQLite-Datenbank als "in-memory" angelegt. Wer Daten persistent speichern möchte, der muss explizit das db-Argument angeben:

>>> c2 = sostore.Collection('people2', db='people.db')

Eine dritte Möglichkeit ist, beim Anlegen einer Instanz einer Collection als Argument eine bestehende DB-Connection anzugeben. Details hierzu sind in den Docstrings von sostore zu finden.

Als erstes werden der Collection ein paar Datensätze hinzugefügt:

>>> d = {'name':'Peter', 'surname':'Punk', 'job':'musician'}
>>> c.insert(d)
{'job': 'musician', '_id': 1, 'surname': 'Punk', 'name': 'Peter'}
>>> c.insert({'name':'Peter', 'surname': 'Pan', 'job': 'fairyhero'})
{'job': 'fairyhero', '_id': 2, 'surname': 'Pan', 'name': 'Peter'}
>>> c.insert({'name':'Otto', 'surname': 'Normal', 'job': 'lawyer'})
{'job': 'lawyer', '_id': 3, 'surname': 'Normal', 'name': 'Otto'}
>>> c.insert({'name':'Volker', 'surname': 'Racho', 'job': 'musicien'})
{'job': 'musicien', '_id': 4, 'surname': 'Racho', 'name': 'Volker'}


Wie zu sehen ist, wird für jedes hinzugefügt Objekt das Objekt sowie die automatisch angelegt ID (welches das Objekt in der SQLite-Datenbank als Primärschlüssel hat) zurück gegeben. Wer möchte, kann auch eine zufällige ID erzeugen, indem bei insert zusätzlich das Argument randomize=True angibt.

Das 'Aktualisieren eines Objekts innerhalb der Collection ist natürlich auch möglich. Im folgenden wird der Rechtschreibfehler beim Job des 4. Datensatzes korrigiert:

>>> d = c.get(4)
>>> d['job'] = 'musician'
>>> c.update(d)
{u'job': 'musician', '_id': 4, u'surname': u'Racho', u'name': u'Volker'}


Das Löschen eines Datensatzes erfolgt wie folgt:

>>> c.remove(3)

Die Anzahl der Datensätze in der Collection liefert count

>>> c.count
3


sostore bietet verschiedene Möglichkeiten, die Daten auszulesen. Um alle Datensätze zu sehen, genügt der Befehl:

>>> c.all()
[{u'job': u'musician', '_id': 1, u'surname': u'Punk', u'name': u'Peter'}, {u'job': u'fairyhero', '_id': 2, u'surname': u'Pan', u'name': u'Peter'}, {u'job': u'musician', '_id': 4, u'surname': u'Racho', u'name': u'Volker'}]


Aber natürlich sind die Datensätze auch einzeln selektierbar. Wer die ID kennt, kann sich diese Daten wie folgt anzeigen lassen:

>>> c.get(1)
{u'job': u'musician', '_id': 1, u'surname': u'Punk', u'name': u'Peter'}


Es ist auch möglich, mehr als eine ID anzugeben:

>>> c.get_many([1, 2])
[{u'job': u'musician', '_id': 1, u'surname': u'Punk', u'name': u'Peter'}, {u'job': u'fairyhero', '_id': 2, u'surname': u'Pan', u'name': u'Peter'}]


Optional kann man auch zusätzlich die Felder angeben, die zurück geliefert werden sollen:

>>> c.get_many([1, 2], fields='surname')
[{u'surname': u'Punk', u'name': u'Peter'}, {u'surname': u'Pan', u'name': u'Peter'}]


Das Argument field=... funktioniert übrigens auch beim weiter oben gezeigten Befehl all().

Nun besteht bei "real-life" Applikation natürlich auch öfters die Notwendigkeit, Daten anhand von Werten in den Datensätzen zu finden. Dies funktioniert so:

>>> c.find_one('surname', 'Punk')
{u'job': u'musician', '_id': 1, u'surname': u'Punk', u'name': u'Peter'}


Diese Funktion liefert immer nur den ersten Treffer zurück, wie der folgenden Befehl zeigt:

>>> c.find_one('name', 'Peter')
{u'job': u'musician', '_id': 1, u'surname': u'Punk', u'name': u'Peter'}


Jetzt gibt es aber zwei Datensätze mit dem Feld "name=Peter" in unserer Collection. Um alle Datensätze, die einem Suchkriterium entsprechen, zu finden, dient der Befehl:

>>> c.find_field('name', 'Peter')
[1, 2]


Das Ergebnis ist eine Liste von IDs. find_field kennt als optionales Argument noch compare_function, welches als Wert eine Vergleichsfunktion enthalten kann. Details findet man in den Docstrings.

Zwei weitere Funktionen von Collection sind random_entry() und random_entries(count=X), welche einen bzw. X zufällige gewählte Datensätze zurück liefern.

Das SQLite Object Store ist mit Sicherheit kein komplexes Modul. Aber es kann doch recht praktisch sein, wenn man Python-Dicts bzw. JSON-Objekt Speichern und Durchsuchen möchte. Außerdem bietet sostore die Möglichkeit die Daten persistent zu sichern.

Keine Kommentare:

Kommentar veröffentlichen