Anfängerfragen zu sqlite und exception handling

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
jovica.aleksic
User
Beiträge: 9
Registriert: Sonntag 17. Januar 2010, 18:59

Hallo.

Ist es "normal" bzw sinnvoll dass man mit so vielen try...except blöcken arbeitet? Sieht irgendwie komisch aus, und manches hiervon ist evtl totaler Schwachsinn, aber naja. Wen soll man fragen, wenn man alleine zuhause lernt und tüftelt :)

Mir ist klar dass ich das Forum evtl unpassend gewählt habe, immerhin frage ich ja nach etwas recht allgemeinem (Einsatz von try...except), aber es ist nun mal doch datenbankbezogen :)

Mir geht es primär um die query() methode:

Code: Alles auswählen

    
class SqliteDB:
    """Executes database queries for the User class"""
    
    def __init__(self, db_file):
        self._db_file = db_file
        try:
            self._conn = sqlite3.connect(self._db_file)
        except Exception, e:
            print "Error: Sqlite connection not available.\n\t%s" % e
    
    @property
    def db_file(self):
        """Gets the database file name or path."""
        return self._db_file
    
    @property
    def conn(self):
        """Gets the connection object."""
        return self._conn   
     
    def query(self, sql=''):  
        # get the db cursor 
        try:
            cursor = self.conn.cursor()
        except Exception, e:
            print "Error: Cursor not available.\n\t%s" % e
            
        # execute query
        try:                 
            cursor.execute(sql)              
        except Exception, e:
            print "Error: SQL query failed.\n\t%s" % e
            
        # commit the query to the database
        try:    
            conn.commit()
        except Exception, e:
            print "Error: SQL query not committed.\n\t%s" % e
            
        # fetch the results
        try: 
            results = cursor.fetchall()
        except Exception, e:
            print "Error: Could not fetch the results.\n\t%s" % e
        
        # close the cursor
        cursor.close()
        
        # return results
        return results 
Bin gespannt auf eure Meinungen.
Mich interessiert auch, ob der Code stilmäßig vernünftig aussieht, bin natürlich am lernen und offen für Verbesserungen.
BlackJack

@jovica.aleksic: Also sinnvoll ist die Fehlerbehandlung da sicher nicht, weil die Fehler nicht wirklich behandelt werden. Mal beim ersten ``try``/``except`` in `query()` angefangen: Wenn die Operation fehlschlägt, dann gibt es keinen Cursor, dann wird auch 100% der Versuch mit dem `execute()` fehlschlagen.

Du musst Dir a) überlegen welche Ausnahmen überhaupt vorkommen können und b) wie man*sinnvoll* im gegebenen Kontext darauf reagieren kann. Wenn man das nicht kann, sollte man es auch gar nicht versuchen, sondern das einem Aufrufer überlassen.

Wenn man so ein `SqliteDB`-Exemplar erstellt, und das `connect()` in der `__init__()` schon fehlschlagen sollte, weil zum Beispiel nicht auf die Datei zugegriffen werden kann, dann bekommt man das als Aufrufer überhaupt nicht mit. Das ist doch ungünstig, dass man dann mit einem Objekt weiterarbeitet, was gar nicht funktionieren *kann*.

Ansonsten sind die `properties()` ziemlich überflüssig. Wenn Du "read only"-Attribute haben möchtest, solltest Du das einfach per Dokumentation regeln.

Und die Kommentare in `query()` haben keinen Mehrwert, weil sie nur das offensichtliche *nochmal* sagen.
jovica.aleksic
User
Beiträge: 9
Registriert: Sonntag 17. Januar 2010, 18:59

Danke für deine Antwort, BlackJack.

Ich muss das Konzept noch besser begreifen und verinnerlichen, dass man dem Aufrufer die Fehlerbehandlung überlässt. Ist natürlich völlig einleuchtend. Habe bisher noch gar nicht "out-of-the-box" gedacht, meine Python-Erfahrungen beschränken sich bisher komplett auf das eine users-Modul, dass ich testweise schreibe.
Mir ist noch nicht ganz klar, was du hiermit meinst:
Wenn man so ein `SqliteDB`-Exemplar erstellt, und das `connect()` in der `__init__()` schon fehlschlagen sollte, weil zum Beispiel nicht auf die Datei zugegriffen werden kann, dann bekommt man das als Aufrufer überhaupt nicht mit. Das ist doch ungünstig, dass man dann mit einem Objekt weiterarbeitet, was gar nicht funktionieren *kann*.

Ich nutze das Ganze folgendemaßen:

Code: Alles auswählen

class User:    
    def __init__(self): 
        self._db = SqliteDB(Settings.SQLITE_PATH)
und dachte mir das so für die Nutzung:

Code: Alles auswählen

try:
    usr = User()
except Exception, e:
    print "Error: Could not create user.\n\t%s" % e
Bin ich damit denn nicht oder nur schlecht "abgesichert"?
Ansonsten sind die `properties()` ziemlich überflüssig. Wenn Du "read only"-Attribute haben möchtest, solltest Du das einfach per Dokumentation regeln.
Sprich einfach ganz normale Attribute nutzen und darauf hinweisen, dass die als read-only genutzt werden sollen..ja? (Simple is beatyful quasi)
Ich habe zuletzt mit Flex gearbeitet, und da arbeitet man irgendwie so übertrieben rigoros beim zurechtdefinieren. Hat leider abgefärbt, aber da ist jetzt Python sicherlich ne gute Gegenmaßnahme :)
Und die Kommentare in `query()` haben keinen Mehrwert, weil sie nur das offensichtliche *nochmal* sagen.
Jo, die waren eigentlich nur für mein Auge da, um eine optische Struktur bei all den try..excepts zu haben! Quasi ein Folgefehler vom Unwissen an erster Stelle :)
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Er meint wohl, dass ein print "Error..." keine wirklich sinnvolle Fehlerbehandlung ist, da das Programm trotzdem weiterläuft.

Insgesamt stellt sich die Frage, ob try/except-Konstrukte in dieser Klasse überhaupt sinnvoll sind, da du die Ausnahmen ja eigentlich in der tatsächlichen Anwendung abfangen willst und nicht dort...

Code: Alles auswählen

try:
    usr = User()
except Exception, e:
    print "Error: Could not create user.\n\t%s" % e
Nebenbei: mit Exception fängst du nahezu alles ab, was es gibt.

Das hier ist wohl auch überflüssig:

Code: Alles auswählen

def query(self, sql=''):
ein leerer query wäre wohl eher sinnlos.

Das hier ist auch nicht wirklich gut...

Code: Alles auswählen

@property
    def conn(self):
        """Gets the connection object."""
        return self._conn
ein einfaches self.conn wäre da besser.
BlackJack

@jovica.aleksic: Also bei dem `User`-Beispiel und der `SQLiteDB` mit Deinen ``except``\s bist Du gar nicht abgesichert. Überleg Dir mal einen Fall wo die Meldung das ein Benutzer nicht angelegt werden überhaupt ausgegeben werden kann!? Ich sehe da keinen.

Und wenn die `SQLiteDB.__init__()` nicht alle Ausnahmen "verschlucken" würde, ist das mit dem `User` nicht genug Kontext um zu sagen ob das eine Absicherung ist, oder auch nur ein Unterdrücken von Fehlern, was ziemlich sicher zu Folgefehlern führt. Die Frage ist doch was mit dem `User` gemacht werden soll. Wenn der nach der Erstellung zwingend notwendig ist, dann darf man ja nicht einfach weitermachen, wenn er nicht angelegt wurde.
jovica.aleksic
User
Beiträge: 9
Registriert: Sonntag 17. Januar 2010, 18:59

Ja ich sehe schon, da fehlt einiges an Erfahrung bei mir...
Ich habe jahrelang im frontend gearbeitet, Flash/Flex und javascript, und bewarb mich zuletzt für einen job "web developer" bei einem ziehmlich hochklassigen unternehmen. Die kamen zurück mit nem test, nähmlich user system mit registrieren, aktivierungs-email schicken, und login system mit sperre nach drei falschen Versuchen einzuloggen. Vorzugsweise mit Python. Das ist natürlich ein backendler der da gesucht wird, und so hab ich mir die Idee eigentlich abgeschmiert - ich bin nie und nimmer professionell einsetzbar in dem Bereich um den es geht.
Dennoch möchte ich die Testaufgabe halt schön lösen, auch wenn ich mich nicht mehr um den Job bewerben will.

Das ist halt das Problem, wenn man irgendwie keine Kenntnisse und keine Erfahrung hat, es aber unbedingt "richtig gut" machen will, sprich irgendwo heißt es Exception Handling ist wichtig, und schon setzt sich der noob dran auf Teufel komm raus Exceptions einzubauen. Nicht zu handeln, wie mir mittlerweile wohl bewusst ist (print als exception handling ist eher lachhaft :) )

Nun, ich danke euch jedenfalls. Mal schauen, ich versuche die genannten Punkte zu berücksichtigen. Und am besten ich mache das Modul fertig, so dass es funktioniert, und erbitte ein wenig code-review in nem separaten post..
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Wenn es dir um das geht, dann würde man das normalerweise auch nicht alles so selbst schreiben. Du versuchst da ja fast einen Art ORM wie SQLAlchemy nachzubauen, warum sowas nicht gleich verwenden? Ich verweise dich einfach mal auf das Tutorial: http://www.sqlalchemy.org/docs/ormtutorial.html
Oder du nimmst gleich ein Allround-Framework wie Django oder ähnliches - ich kann mir auch nicht vorstellen, dass die Aufgabestellung ist das alles selbst neu zu erfinden.
jovica.aleksic
User
Beiträge: 9
Registriert: Sonntag 17. Januar 2010, 18:59

naja, ich will ja kein echtes großes Projekt aufbauen. Ich habe eine recht simple Aufgabenstellung, nämlich mach ein minimales user-system zurecht, in einem modul. Wie gesagt, register, activate, login, (login-tracken zwecks account sperren), das wars.
Weiß nicht ob es da Sinn macht, ein framework oder dergleichen zu nehmen. Bis ich Django installiert hab bzw ich mich mit seinen Eigenheiten vertraut gemacht hab, wie macht man was etc, vergeht ja auch extra Zeit...wobei Django ja sicherlich poulär ist, und es ratsam ist, sich damit auszukennen. Naja mal schauen, werde mich mal etwas umschauen in die Richtung.

Danke für den Link zum Object Relational Tutorial, sieht interessant aus!
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

Wobei du sicherlich keine Werbung für dich machst, wenn du das alles low-level selbst schreibst. So ein minimales Login-System, das du da willst, ist mit Django in wenigen Zeilen Code umgesetzt. Normalerweise setzt man auf gute, bereits bestehende Komponenten.
BlackJack

@SchneiderWeisse: Es kommt hier auch ein bisschen auf die Ausgangsfrage an denke ich. Wenn Du in einem Bewerbungsgespräch sagst, dass Du da auf ein ORM oder noch besser eine bestehende Bibliothek setzt, ist das vielleicht eine richtige Antwort, es kann aber trotzdem sein, dass die nächste Frage dann ist, "Aber skizzieren sie doch mal einen Entwurf, wenn sie das selber implementieren müssten." Einfach um zu sehen, ob man das *könnte*, und wie man sich dabei anstellt.
nemomuk
User
Beiträge: 862
Registriert: Dienstag 6. November 2007, 21:49

@BlackJack: das kann natürlich gut sein, da stimme ich dir zu!
Antworten