Datenbanken
Für einfache Datenbankanwendungen mit Python bietet sich sqlite
an, welches eine abgespeckte
Version einer SQL-Datenbank (mySQL, mariaDB, ...) ist. Dies hat den Vorteil, dass man das Problem der
Zugriffsrechte etwas umgehen kann. So erfordert beispielsweise die Installation und der Betrieb einer
Datenbank in der ZEDAT, dass man sich auf dem Login-Rechner der ZEDAT befindet oder den ZUgriff von Webseiten,
die auf einem User-Account liegen, vornimmt. sqlite
unterstützt die folgenden Datentypen:
INTEGER, REAL, TEXT, BLOB und NULL. Theoretisch auch noch DATE, wobei dieses aber intern als TEXT gespeichert
wird. Datumsoperationen müssen daher auf Python-Ebene erfolgen und nicht mit Datenbankbefehlen.
Zuerst wird die Datenbank mit einer entsprechenden Struktur erstellt. Sollte die Datenbank in einem Unterverzeichnis liegen, so muss dieses vorher erstellt werden, was allerdings auch aus Python heraus geschehen könnte.
Die sql-Anweisung muss in den Mehrzeilenkommentar """ .... """
gesetzt werden, wenn
die Parameter über mehrere Zeilen gehen. Grundsätzlich besteht der Ablauf aus immer den gleichen Schritten:
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("kursliste.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) verbindung.commit() verbindung.close() # Zur Kontrolle kann die Dateiliste ausgegeben werden: #import os #files = [f for f in os.listdir('.') if os.path.isfile(f)] #for f in files: # print(f)
Nach Ausführen dieses Programmes existiert im angebenen Verzeichnis die leere Datenbank:
# Hinweis voss@h2710458:~/tmp/$ ls -la insgesamt 16 drwxrwxr-x 2 voss voss 4096 Feb 1 21:20 . drwxrwxr-x 22 voss voss 4096 Feb 1 21:20 .. -rw-r--r-- 1 voss voss 8192 Feb 1 21:20 kursliste.db
Möchte man diese komplett löschen, so geht das auch aus Python heraus. Damit können alle folgenden Beispiele wieder mit einer leeren Datenbank beginnen.
import os # operating system try: os.remove("/tmp/kursliste.db") except OSError as err: print ("Fehler: %s - %s." % (err.filename, err.strerror)) else: print("File removed successfully")
Wenn Sie dieses selbst ausprobieren wollen, nehmen Sie einen Namen für die Datenbank, der eindeutig ist, ansonsten wird die
Datenbank sofort wieder überschrieben, wenn jemand anderes das Programm laufen lässt. Ein sinnvoller Name wäre beispielsweise: kurs-voss.db
. Dann erkennt jeder,
was passieren dürfte, wenn er denselben Namen verwendet. Das folgende Beispiel fügt einige Einträge in die Datenbank ein,
wobei hier noch eine ziemlich umständliche Methode gewählt wurde.
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("/tmp/kursliste2.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) sql_anweisung = "INSERT INTO personen VALUES ('Erasmus von', 'Rotterdam', 'Politik', '12.10.1713', '1234')" sql.execute(sql_anweisung) sql_anweisung = "INSERT INTO personen VALUES ('Herbert', 'Voß', 'Informatik', '1.10.1975', '12341234')" sql.execute(sql_anweisung) sql_anweisung = "INSERT INTO personen VALUES ('Johnny', 'Mauser', 'Biologie', '2.3.2001', '7643876')" sql.execute(sql_anweisung) verbindung.commit() # Namen sollten jetzt definitiv gespeichert sein print("Ausgabe der Namen:") sql.execute ("SELECT * FROM personen") db_liste = sql.fetchall() print(db_liste) verbindung.close()
Die Einträge für die Datenbank können auch zwischendurch in Python-Variablen gespeichert werden, um sie dann an die Datenbank zu übergeben. Dies geschieht über einen zweiten Parameter, der dafür sorgt, dass die Fragezeichen als Platzhalter durch die richtigen Werte ersetzt werden.
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("/tmp/kursliste.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) Vorname = "Heinrich" Nachname = "Böll" Fach = "Literaturwissenschaft" Eintritt = "23.11.1975" Matrikel = "1236547" sql.execute("INSERT INTO personen VALUES (?,?,?,?,?)", (Vorname, Nachname, Fach, Eintritt, Matrikel) ) print("Ausgabe der Namen:") sql.execute ("SELECT * FROM personen") db_liste = sql.fetchall() for zeile in db_liste: print(zeile) verbindung.commit() verbindung.close()
Es besteht auch die Möglichkeit eine komplette Liste zu übergeben, wobei diese logischerweise
die Struktur der Datenbank abbilden muss. Anstell von sql.execute
wird dann der
Befehl sql.executemany
gewählt:
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("/tmp/kursliste.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) studies = [('Georg Wilhelm Friedrich', 'Hegel', 'Philosophie', '27.08.1795', '187'), ('Johann Christian Friedrich', 'Hölderlin', 'Germanistik', '20.03.1801', '96'), ('Rudolf Ludwig Carl', 'Virchow', 'Medizin', '13.10.1845', '286')] sql.executemany("INSERT INTO personen VALUES (?,?,?,?,?)", studies ) print("Ausgabe der Namen:") sql.execute ("SELECT * FROM personen") db_liste = sql.fetchall() for zeile in db_liste: print(zeile) verbindung.commit() verbindung.close()
Die einzelnen SQL-Befehle sind hier zwar nicht Inhalt des Kurses, dennoch sollen einige Möglichkeiten der Ausgabe gezeigt werden.
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("/tmp/kursliste.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) sql.execute ("SELECT nachname, vorname FROM personen") db_liste = sql.fetchall() print("Ausgabe der Namen:") for zeile in db_liste: print(zeile) print() sql.execute("SELECT nachname, vorname, studienfach FROM personen ORDER BY nachname") db_liste = sql.fetchall() for zeile in db_liste: print(zeile) print() sql.execute("SELECT nachname, vorname, studienfach FROM personen ORDER BY nachname DESC")# ASC ist Vorgabe db_liste = sql.fetchall() for zeile in db_liste: print(zeile) verbindung.close()
Als Anwendungstest soll eine Datenbank mit allen Passagieren der gesunkenen Titanic erstellt werden. Eine kommaseparierte Liste der Passagiere steht zur Verfügung. Die Liste enthält dabei folgende Einträge (hier die ersten beiden Zeilen der Liste):
PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked 1,0,3,"Braund, Mr. Owen Harris",male,22,1,0,A/5 21171,7.25,,S
Die Datenbank kann jederzeit erweitert, gekürzt oder geändert werden. Die Erweiterung entspricht dem obogen Vorgehen und das Löschen wird in der Regel an eine Bedingung geknppft, um nicht versehentlich zu viele oder gar alle Einträge zu löschen. Im folgenden Beispiel wird nur der Vorname geändert, wenn der Nachname "Rotterdam" lautet:
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("/tmp/kursliste3.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) sql.execute("SELECT nachname, vorname, studienfach FROM personen ORDER BY nachname DESC")# ASC ist Vorgabe db_liste = sql.fetchall() print("Namensliste vorher:") for zeile in db_liste: print(zeile) nachname = "Rotterdam" vorname = "Desiderius Erasmus von"# war nur Erasmus von sql.execute("UPDATE personen SET vorname=? WHERE nachname=?", (vorname, nachname)) sql.execute("SELECT nachname, vorname, studienfach FROM personen WHERE nachname='Rotterdam'")# ASC ist Vorgabe db_liste = sql.fetchall() print() print("Namensliste nachher:") for zeile in db_liste: print(zeile) verbindung.commit() verbindung.close()
Insbesondere beim Löschen von Datenbankeinträgen ist es ratsam Vorsicht walten zu lassen, denn eine falsche Option kann zum Verlust der gesamten Datanbank führen. Ein Backup ist immer sinnvoll! In der Regel wird man die zu löschenden Einträge dem Schlüsselwort "WHERE" übergeben. Das folgende Beispiel löscht "Johnny Mauser" aus der Tabelle. Aus Gründen der soeben erläuterten Vorsicht, wird sowohl nach dem Vornamen als Nachnamen gefragt, obwohl das für diese Demonstrationstabelle uninteressant wäre, denn es gibt nur einen Johnny und einern Mause ...
import sqlite3 # braucht Modul sqlite3-api verbindung = sqlite3.connect("/tmp/kursliste.db") sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30), eintrittFU DATE, matrikel VARCHAR(10));""" sql.execute(sql_anweisung) sql.execute("SELECT nachname, vorname, studienfach FROM personen ORDER BY nachname DESC")# ASC ist Vorgabe db_liste = sql.fetchall() print("Namensliste vorher:") for zeile in db_liste: print(zeile) sql.execute("DELETE FROM personen WHERE nachname='Mauser' AND vorname='Johnny'") sql.execute("SELECT * FROM personen ORDER BY nachname") db_liste = sql.fetchall() print() print("Namensliste nachher:") for zeile in db_liste: print(zeile) verbindung.commit() verbindung.close()
Kursbeispiel:
import os import sqlite3 import sys def switch(argument): if argument == "a": ausgabe() elif argument == "e": eingabe() elif argument == "l": loeschen() elif argument == "d": del_datenbank() def del_datenbank(): pass def ausgabe(): print("Ausgabe der Namen:") sql.execute ("SELECT * FROM personen") db_liste = sql.fetchall() for zeile in db_liste: print(zeile) def oeffneDB(db): eingabe = input("Bitte Datenbank angeben ["+db+"]: ") if eingabe != "": db = eingabe """ if not os.path.exists("daten\kursliste.db"): print("Datei existiert nicht!") eingabe = input("Erstellen? [J]: ") """ verbindung = sqlite3.connect(db) sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS personen ( vorname VARCHAR(20), nachname VARCHAR(30), studienfach VARCHAR(30));""" sql.execute(sql_anweisung) return sql, verbindung def loeschen(): pass def eingabe(): while True: NN = input("Nachname: ") if NN == "": return VN = input("Vorname : ") SF = input("Fach : ") sql.execute("INSERT INTO personen VALUES (?,?,?)", (NN, VN, SF) ) def waehle(): wahl = input("Auswahl: E)ingabe, A)ausgabe, Eintrag L)öschen, D)B löschen, Q)uit") return wahl.lower() sql, verbindung = oeffneDB("daten/kursliste.db") while True: wahl = waehle() print(wahl) if wahl != "q": switch(wahl) else: break verbindung.commit() verbindung.close()
Beispiele
Beispiel 1 (Grafische Eingabefenster)
from tkinter import * fields = 'Vorname','Nachname','Straße','PLZ','Wohnort','Telefon','e-Mail' entries = {} def fetch(): for field in fields: print ('Feld %s => "%s"' % (field,entries[field].get())) # hole Text def makeform(root, fields): for field in fields: textStr = StringVar() textStr.set(field) row = Frame(root) # eine neue Zeile lab = Label(row, width=15, text=field) # label, entry ent = Entry(row,text=textStr) row.pack(side=TOP, fill=X) # packe row on top lab.pack(side=LEFT) ent.pack(side=RIGHT, expand=YES, fill=X) # horizontal streckem entries[field] = textStr if __name__ == '__main__': root = Tk() root.title('Eingabemaske') makeform(root, fields) Button(root, text='nächster Eintrag',command=fetch).pack(side=LEFT) Button(root, text='Ende',command=quit).pack(side=RIGHT) root.mainloop()
Beispiel 2 (Webbasierte Adressdatenbank)
Muss als adressen.cgi
auf dem ZEDAT-Rechner gespeichert werden.
#!/usr/bin/python3 # -*- coding: utf-8 -*- import os import cgi import cgitb; cgitb.enable() #import psycopg2 import sqlite3 #import html #import sys print("Content-Type: text/html; charset=utf-8") print() #print(sys.stdout.encoding) fs = cgi.FieldStorage() daten = False delete = None if fs: # Daten vorhanden? vorname = fs.getvalue("vorname", None) nachname = fs.getvalue("nachname", None) strasse = fs.getvalue("strasse", None) plz = fs.getvalue("plz", None) ort = fs.getvalue("ort", None) email = fs.getvalue("email", None) info = fs.getvalue("info", None) delete = fs.getvalue("delete",None) if delete == None: daten = True # Connection öffnen verbindung = sqlite3.connect("adressen.db") #verbindung.text_factory = str sql = verbindung.cursor() sql_anweisung = """ CREATE TABLE IF NOT EXISTS adressen ( no VARCHAR(10), vorname VARCHAR(50), nachname VARCHAR(50), strasse VARCHAR(50), plz VARCHAR(10), ort VARCHAR(50), email VARCHAR(50), info VARCHAR(300) );""" sql.execute(sql_anweisung) if delete == None: if os.path.exists("adressen.id"): idFile = open('adressen.id', 'r') maxNo = int(idFile.readline().rstrip('\r\n')) idFile.close() os.remove("adressen.id") maxNo += 1 else: maxNo = 1 idFile = open('adressen.id','w') idFile.write(str(maxNo)) idFile.close() # Neuen Datensatz anlegen if daten == True: sql.execute("INSERT INTO adressen VALUES (?,?,?,?,?,?,?,?)", (maxNo, vorname, nachname, strasse, plz, ort, email, info)) verbindung.commit() elif delete != None: sql_anweisung = ('DELETE FROM adressen WHERE no='+delete+";") sql.execute(sql_anweisung) # Meldung zurück geben print(''' <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>Adressdatenbank</title> </head> <body> ''') if daten: print("<h1>Fertig</h1> <p>Die neue Adresse wurde erstellt.</p> <p>Die Liste:</p>") else: print("<h1>Aktuelle Datenbank</h1> <p></p>") #print("<p>",vorname, nachname, strasse, plz, ort, email, info,"</p>\n") print('<form action="adressen.cgi">') print('<table border="1"><tr><th>ID</th><th>Vorname</th><th>Nachname</th><th>Straße</th><th>PLZ</th><th>Ort</th><th>E-Mail</th><th>Info</th></tr>') sql.execute("SELECT * FROM adressen") #print("Erstelle Liste") db_list = sql.fetchall() #print(db_list) for line in db_list: print('<tr><td>',end="") print('''<input type="button" name="loeschen" style="BACKGROUND-COLOR:#555555; COLOR:white;" value="''',end="") print(line[0],end="") print('''" onClick="location.href='adressen.cgi?delete= ''',end="") print(line[0],end="") print(""" ';"></td>""") for column in line[1:]: print('<td>',column,'</td>') print("</tr>\n") print(''' </table> </form> <p></p> <p>Zum Löschen auf die entsprechende Nummer klicken! Vorsicht! Es erfolgt keine Nachfrage!!!</p> <p><a href="adressen.html">Zurück zur Eingabe</a></p> </body> </html>''') # Connection schließen verbindung.commit() verbindung.close()
Muss als adressen.html
auf dem ZEDAT-Rechner gespeichert werden.
# Hinweis <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Kleine Adressdatenbank</title> </head> <body> <h1>Füllen Sie bitte folgende Felder aus</h1> <form action="adressen.cgi" accept-charset="utf-8"> <table border="0" cellpadding="5" cellspacing="0" bgcolor="#E0E0E0"> <tr> <td align="right">Vorname:</td> <td colspan="4"><input name="vorname" type="text" size="30" maxlength="30"></td> </tr> <tr> <td align="right">Nachname:</td> <td colspan="4"><input name="nachname" type="text" size="40" maxlength="50" accept-charset="utf-8"></td> </tr> <tr> <td align="right">Straße:</td> <td colspan="4"><input name="strasse" type="text" size="30" maxlength="40" accept-charset="utf-8"></td> </tr> <tr> <td align="right">Postleitzahl:</td> <td><input name="plz" type="text" size="6" maxlength="10"><td> <td align="right">Ort:</td> <td><input name="ort" type="text" size="40" maxlength="50"></td> </tr> <tr> <td align="right">E-mail Adresse:</td> <td colspan="4"><input name="email" type="text" size="30" maxlength="40"></td> </tr> <tr> <td align="right" valign="top">Zusätzliche Informationen:</td> <td colspan="4"><textarea name="info" rows="10" cols="50" value="--"></textarea></td> </tr> <tr> <td align="right">Daten:</td> <td colspan="4"> <input type="submit" value=" Absenden "> <input type="reset" value=" Abbrechen"> </td> </tr> </table> </form> </body> </html>
Die Datei adressen2.cgi (als zip)
Beispiel 3 (Webserver)
Server.zip herunterladen und entpacken. Danach serv.py
starten und im Webbrowser
http://localhost:8080
aufrufen.
Nächste Einheit: 10 OOP