9.3 DATENBANK-MANAGER

 

 

EINFÜHRUNG

 

Im Umgang mit Datenbanken wird meistens ein benutzerfreundliches Dialogfenster verwendet, damit ein Anwender die wichtigsten Datenbankmanipulationen ohne Programmierkenntnisse durchführen kann. Die wichtigsten Manipulationen sind:

  • Durchlaufen der Datensätze (Navigation)
  • Suchen nach bestimmten Datensätzen (Search)
  • Aufnehmen von neuen Datensätzen (Insert)
  • Mutieren von Datensätzen (Update)
  • Löschen von Datensätzen (Delete)
  • Ausschreiben/Ausdrucken von Datensätzen (Report)

 
Die Erstellung von GUI-Dialogen ist im Allgemeinen eine zeitraubende Tätigkeit. Unter Verwendung der Klasse EntryDialog bietet TigerJython aber die Möglichkeit, standardisierte Dialoge mit Eingabefeldern und Buttons verhältnismässig einfach zu erzeugen.

Trotzdem ist das Programm DbManager.py zu lang, um als Ganzes publiziert zu werden. Du kannst es aber von download/dbmanager.zip downloaden und dann in den TigerJython-Editor kopieren. Es verwendet die Datenbank schule.db und die Tabelle person, wie du sie im vorhergehenden Kapitel erzeugt hast. Die Tabelle sollte zu Beginn auch bereits die 5 Datensätze enthalten, die du mit dem Programm im vorhergehenden Kapitel erzeugt hast.

 

 

DEN DATENBANK-MANAGER VERWENDEN

 

Personendaten  werden üblicherweise in einem Eingabedialog aufgenommen und dann in der Datenbank eingefügt. Beim Start des Managers wird ein Dialogfenster mit der ersten Person angezeigt. (Wenn die Datenbank noch keine Datensätze enthält ist, sind die Eingabefelder unter Information leer.)



Die Entwicklung von GUI-basierten Programmen erfordert eine konsequente Bedienungslogik, die sich an die üblichen Standards hält. Insbesondere sollte der Benutzer immer eine Rückmeldung über Erfolg oder Misserfolg seiner Aktionen erhalten. Dazu eignet sich eine Statuszeile gut. Wichtig ist auch, dass das Programm sogar bei unvernünftigen Eingaben niemals abstürzt. Hier ist folgende Bedienungslogik implementiert:

  • Beim Start wird man bei einer leeren Datenbank in den Insert-Modus versetzt. Sonst befindet man sich im Navigations-Modus und es wird der erste Datensatz angezeigt. Man kann mit den Navigationsbuttons durch die Einträge blättern. Sie sind alphabetisch bezüglich Familien- und Vorname sortiert
  • Klickt man Insert, so gelangt man in den Insert-Modus. Dabei werden leere Datenfelder angezeigt, die man ausfüllen muss. Alle Buttons ausser  Save und Cancel werden deaktiviert  Drückt man Save, so werden die Eingaben auf Legalität überprüft (Validation). Sind sie korrekt, so wird der Datensatz in die Datenbank geschrieben und der Insert-Modus verlassen. Der eingegebene Datensatz bleibt angezeigt und ist alphabetisch richtig eingeordnet. Drückt man Cancel, so werden die Eingaben ignoriert und man kehrt vom Insert-Modus in den Navigations-Modus zurück.  Es wird der erste Datensatz angezeigt. Ist die Datenbank leer, so erscheint eine Fehlermeldung. Zum Abbruch des Programms drückt man den Close-Button der Titelzeile
  • Mit Delete löscht man den angezeigten Datensatz (ohne Rückfrage) und es wird der erste Datensatz gezeigt. Gibt es nach dem Löschen keine Datensätze mehr, so wird man in den Insert-Modus versetzt.

Das Programm ist strukturiert aufgebaut und enthält zum besseren Verständnis einige Kommentare. Im Folgenden werden wichtige Implementierungsverfahren erläutert.

 

 

EREIGNISSTEUERUNG

 

Der Einfachheit  halber werden in der Klasse EntryDialog Button-Ereignisse nicht wie üblich mit Callbacks erfasst, sondern jedes Button-Objekt hat eine Methode isTouched(), die True zurückliefert, wenn ein Button geklickt wurde. Um die Button-Ereignisse zu erfassen, lässt du daher das Hauptprogramm in einer Ereignisschleife laufen, in der du alle Buttons abfragst, und rufst jeweils die entsprechenden Funktionen doFirst(), doLast(), usw. auf, in denen du die entsprechenden Aktionen ausführst.

while not dlg.isDisposed():
    if firstBtn.isTouched():
         doFirst()
    if lastBtn.isTouched():
         doLast()
    if nextBtn.isTouched():
         doNext()  
    ...

Die Ereignisschleife wird abgebrochen, wenn du mit dem Close-Button der Titelzeile das Dialogfenster schliesst.

 

 

NAVIGIEREN IM RESULTSET

 

Beim Navigieren durch die Datensätze holst du zuerst mit einem SELECT-Befehl die Datensätze als Resultset. Da es in SQLite nicht möglich ist, im Resultset nach vorne und wieder zurück zu navigieren, kopierst du den Resultset mit resultSet = cursor.fetchall() in eine globale Python-Liste, in der jeder Datensatz als Tupel enthalten ist:

[(4, 'Bauer', 'Paul', 'Muri', 'm', 14), ... ]

Zudem speicherst du den Index des aktuell angezeigten Datensatzes in der globalen Variablen currentIndex, damit du jederzeit weisst, welcher Datensatz gerade angezeigt wird und du leicht auf die Daten zugegriffen kannst, beispielsweise auf den Familiennamen des aktuellen Datensatzes mit

fName = resultSet[currentIndex][1]

 

 

EINFÜGEN EINES NEUEN DATENSATZES

 

Beim Klicken auf den Button Insert wird das System in einen speziellen Zustand versetzt, bei dem die globale Variablen isInsertMode  auf True gesetzt ist. Die Eingabefelder werden gelöscht und die Navigation unterbunden. Beim Drücken von Save untersuchst du zuerst die Eingabewerte mit der Funktion validate() auf offensichtliche Fehler und schreibst eine gegebenenfalls Fehlermeldung aus. Ein Datensatz mit gleichem Familien- und Vornamen wird ebenfalls zurückgewiesen, damit eine Eindeutigkeit der Personen bezüglich ihres vollen Namens gewährleistet ist. Dies ist besonders beim Suchen wichtig, weil es nur eine einzige Person mit einem bestimmten Familien- und Vornamen geben sollte.

 

 

SUCHEN, WILDCARDS

 

Beim Klicken auf den Button Search werden Familien- und Vorname aus den Search-Eingabefeldern ausgelesen und mit einem SELECT-Befehl danach gesucht. Dabei wird aber nicht auf exakte Übereinstimmung geprüft, sondern LIKE verwendet. Damit können im Suchnamen auch Wildcards % verwendet werden, die an Stelle von beliebig vielen Buchstaben stehen. Es kann auch der Wildcard _ verwendet werden, der für genau einen Buchstaben steht.  Der entsprechende SQL-Befehl lautet:

"SELECT * FROM person WHERE familienname LIKE '%s' 
 AND vorname LIKE '%s'" %(familienname, vorname)

Gibt es mehrere Datensätze, welche die Suchbedingung erfüllen, wird der erste Datensatz des Resultsets angezeigt.

 

 

DATENSATZ MUTIEREN ODER LÖSCHEN

 

Ein angezeigter Datensatz kann auch verändert werden. Dabei wird der SQL-Befehl

UPDATE person SET  familienname =  ...

verwendet.  Beim Löschen wird der SQL-Befehl

DELETE FROM person WHERE familienname = ...

verwendet. Bei beiden ist es wichtig, dass Familien- und Vorname den Datensatz eindeutig identifizieren.

 

 

MEMO

 

Im Gegensatz zu einer Tabellenkalkulation spielt die Reihenfolge der Datensätze (Zeilen) in der Datenbank keine Rolle und sollte nie ein wichtiges Kriterium sein. Darum darf die Reihenfolge im Resultset eines SELECT-Befehls nur dann eine Rolle spielen, wenn mit ORDER eine bestimmte Reihenfolge erzwungen wird. Dies ist in der Funktion getAllRecords() mit dem SQL-Befehl

SELECT * FROM person ORDER BY familienname, vorname

der Fall, wo zuerst mit der alphabetischen Reihenfolge der Familiennamen und bei Gleichheit mit der Reihenfolge der Vornamen geordnet wird. Bei Namen mit Akzenten ist allerdings die Reihenfolge problematisch.

 

 

AUFGABEN

 

1.


Gib die Personendaten deiner Klasse ein und speichere die Datenbankdatei an einem sicheren Ort, damit du sie immer wieder verwenden kannst.


2.


Führe einige Mutationen mit dieser Datenbank aus: Datensätze löschen, Personendaten ändern, usw.