Seite 1 von 1

SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:03
von mcdaniels
Guten Morgen!
Es war sehr sinnvoll wieder zurück zu schalten, dann ich habe (wie ich soeben feststellte) wohl u.a. ein Problem bei den Rückgabewerten einer Funktion.

Wenn ich folgendes schreibe:

Code: Alles auswählen

def db_sqlite_verbindung():
    import sqlite3
    db_name = "c:/gb_sqlite.db"
    verbindung = sqlite3.connect(db_name)
    schreibmarke = verbindung.cursor()
    return schreibmarke
Erstelle ich ein Verbindungsobjekt "verbindung" und ein cursor-Objekt "schreibmarke". Müsste ich nach Funktionsaufruf von db_sqlite_verbindung() nicht (ausserhalb der Funktion) mit der schreibmarke weiterarbeiten können?

LG
Daniel

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:07
von deets
Als allererstes: gewoehn dir mal bitte sofort ab, importe lokal in Funktionen zu machen. Dafuer gibt es gelegentlich Gruende. Aber niemals fuer Anfaenger.

Zu deiner Frage: ja, und da du nicht schreibst, was *nicht* geht sehe ich auch nicht, was dazu mehr zu sagen waere.

Doch generell ist dein Vorgehen nicht korrekt, denn du verlierst die Verbindung. Und damit kannst du mit deinem Cursor nur bis zum naechsten Commit arbeiten.

Wenn du mittels Django oder SQLAlchemy und deren ORMs arbeiten wuerdest, koenntest du stattdessen darauf bauen, dass die mit Verbindungs & Transaktionsmanagement daherkommen, und dich mal auf *deinen* Code konzentrieren.

Es ist ja durchaus loeblich, Dinge verstehen zu wollen. Aber du wirkst gerade wie eine etwas aus der Bahn geratene Flipperkugel...

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:23
von BlackJack
@mcdaniels: Ja das müsstest Du. Wieso? Kannst Du nicht? Allerdings nur lesend, denn ohne das Verbindungsobjekt kannst Du keine Veränderungen „commmit”en.

Ich würde es übrigens nicht `schreibmarke` nennen. Er wird eher selten dazu verwendet tatsächlich etwas an der Stelle zu schreiben oder zu verändern an der er sich gerade befindet. Die DB-API unterstützt das auch gar nicht erst.

Was Dir noch passieren kann — das habe ich jetzt nicht ausprobiert — ist das der Programmierer von `sqlite3` nicht damit gerechnet hat, dass jemand das Verbindungsobjekt weg wirft, obwohl er noch einen Cursor davon benutzen möchte, und deshalb beim Abräumen des Verbindungsobjekts die Verbindung automatisch geschlossen wird.

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:27
von mcdaniels
Hallo,
Aber du wirkst gerade wie eine etwas aus der Bahn geratene Flipperkugel...
:lol:
Du hast insofern recht, als dass ich mich mal hier und mal dort "aufhalte". Das ist aber nicht zielführend, wie ich vor Kurzem feststellte.
Ich will NICHT weiter mit Django arbeiten, solange ich derlei Probleme habe. Es soll ja kein offizielles Projekt werden sondern nur ein "Grundlagen pauken".

Was ich eigentlich mit dem Return (dem cursor) machen wollte war zb das hier:

Code: Alles auswählen

schreibmarke.execute(...)
Jedoch kennt Python ausserhalb der Funktion wohl schreibmarke nicht (NameError: 'schreibmarke' is not defined)

@Blackjack:
dass jemand das Verbindungsobjekt weg wirft, obwohl er noch einen Cursor davon benutzen möchte, und deshalb beim Abräumen des Verbindungsobjekts die Verbindung automatisch geschlossen wird.
D.h. ich sollte auch die das "Verbindungsobjekt" per return zurück geben?

LG
Daniel

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:33
von deets
Wie du meinst. Dein Fehler ist ohne groesseren Kontext aber eh nicht zu verstehen. Dir ist aber schon klar, dass du Rueckgabewerte einem Namen zuweisen musst, damit du sie benutzen kannst? Und nochmal wiederholt: du arbeitest falsch, das zentrale Objekt einer DB ist die Verbindung. Die darfst du so nicht wegwerfen.

Code: Alles auswählen


def connect(uri):
      return sqlite.connect(uri)

def tuwas(conn):
      c  = conn.cursor()
      c.execute("some sql")
      return c


die_verbindung = connect(uri) # rueckgabewerte muessen gebunden werden

for row in tuwas(die_verbindung):
     print row
Ich habe bewusst andere Namen fuer den Rueckgabewert conn gewaehlt, damit du nicht denkst, bloss weil etwas den selben Namen hat, waere es das gleiche. In Python sind (gottlob) nicht alle Namen globale Variablen...

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:36
von Hyperion
Du musst den Rückgabewert einer Funktion auch an einen Namen binden!

Code: Alles auswählen

def foo(name):
    s = "{}, the mighty!".format(name)
    return s

# so geht 's natürlich nicht!
foo("Hyperion")
print(s)

# so ist es richtig:
s = foo("Hyperion")
print(s)
`s` ist nur innerhalb der Funktion verfügbar. Außerhalb muss ich einen *neuen* Namen hinschreiben, an den ich das Rückgabe-Objekt binde. In diesem Falle wähle ich auch `s` - das ist aber nicht dasselbe `s` wie innerhalb der Funktion!

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 07:38
von mcdaniels
Dir ist aber schon klar, dass du Rueckgabewerte einem Namen zuweisen musst
Jetzt wieder, ja... Danke!

Das war in diesem Fall der "Fehler".

Dabei hatte ich das alles schon mal gewusst und wieder vergessen...

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 11:14
von mcdaniels
Hallo nochmals,
hätte da noch eine Frage betreffend dem Einfügen von Daten in die SQLite DB.

Ich habe eine DB mit folgenden Tables erstellt:

Code: Alles auswählen

CREATE TABLE guestbook (name text, kommentar text)
Nun wollte ich per:

Code: Alles auswählen

def eingabe():
    data = []
    name = raw_input('Name: ')
    comment = raw_input('Kommentar: ')
    data.append(name)
    data.append(comment)
    for elements in data:
        my_cursor.execute('INSERT INTO guestbook VALUES (?,?)', elements)
    my_db_connection.commit()
Daten in die DB schreiben, erhalte jedoch diesen Fehler:
my_cursor.execute('INSERT INTO guestbook VALUES (?,?)', elements)
ProgrammingError: Incorrect number of bindings supplied. The current statement uses 2, and there are 6 supplied.

D.h. bei der Eingabe (nämlich Daniel bei Name) wird jeder einzelne Buchstabe gezählt.

Nun heißt es, dass die cursor.execute() Methode als 2ten Parameter eine Sequenz erwartet. Mit dem kann ich im Moment nichts anfangen.

Was ist damit genau gemeint?

LG
Daniel

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 11:29
von Hyperion
mcdaniels hat geschrieben: Nun heißt es, dass die cursor.execute() Methode als 2ten Parameter eine Sequenz erwartet. Mit dem kann ich im Moment nichts anfangen.
Da stolpert man in der Doku ja fast schon drüber ;-) Sequence Types

Eine Liste gehört demzufolge dazu.

Aber... wieso zum Geier iterierst Du über diese Liste? Du willst doch gerade die Liste "am Stück" als Parameter übergeben. Wie kommst Du also auf diese Idee?

Das Binden an die beiden Namen ist auch unnötig, wenn Du diese einfach nur in einen Container packst:

Code: Alles auswählen

    name = raw_input('Name: ')
    comment = raw_input('Kommentar: ')
    data.append(name)
    data.append(comment)
# besser:
data.append(raw_input('Name: '))
data.append(raw_input('Kommentar: '))
Du kannst Dir generell merken, dass man sich das Binden an einen Namen sparen kann, wenn man diesen nicht, oder nur genau einmal, verwendet. Ausnahmen sind hier Ausdrücke, die sonst zu lange werden und man durch das Trennen von Aufrufen ein wenig mehr Übersicht bekommt.

Wobei ich es hier umständlich finde, hier eine Liste zu generieren. Ich würde die Sequenz (und dann auch effizienter als Tupel!) erst on-the-fly im `execute` erstellen:

Code: Alles auswählen

my_cursor.execute('INSERT INTO guestbook VALUES (?, ?)', (name, comment))
Generell solltest Du darüber nachdenken, ob diese API, wie Du sie bisher hast, wirklich so flexibel ist! Du koppelst im Moment die Art und Weise der Dateneingabe an das Anlegen eines neuen Datensatzes - das würde ich trennen! Baue eine eigenständige Funktion `add_entry` o.ä. und erledige da nur das Speichern in die DB. Eine Benutzer-Eingabe-Funktion erstellst Du dann separat. Damit kannst Du auch auch anderen Wegen Einträge hinzufügen, etwa zum Testen o.ä. Später kommen Deine Daten dann ja wohl auch aus einem Formular - dann brauchst Du die Funktion zum Speichern nicht noch einmal umbauen, sondern kannst die so lassen, wie sie ist :-)

`my_db_connection` und `my_cursor` lassen "schlimmes" erahnen; wieso sind das zwei globale Variablen? Wieso übergibst Du diese nicht als Parameter?

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 15:34
von mcdaniels
Hallo,
Aber... wieso zum Geier iterierst Du über diese Liste?
Dachte das wäre richtig so... Hatte ich hier gefunden: http://www.hdm-stuttgart.de/~maucher/Py ... QLite.html (Punkt 8.3) entweder is das auf der Seite Humbug, oder ich habe es falsch verstanden.
`my_db_connection` und `my_cursor` lassen "schlimmes" erahnen; wieso sind das zwei globale Variablen? Wieso übergibst Du diese nicht als Parameter?
Werd ich mir nochmal anschauen...

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 15:49
von Hyperion
mcdaniels hat geschrieben: Dachte das wäre richtig so... Hatte ich hier gefunden: http://www.hdm-stuttgart.de/~maucher/Py ... QLite.html (Punkt 8.3) entweder is das auf der Seite Humbug, oder ich habe es falsch verstanden.
Du musst vor allem darüber nachdenken, ob denn Dein Code dem der Seite ähnelt! Wenn Du dort auf Abschnitt 8.3 anspielst, so sollte Dir der Unterschied in der Datenstruktur auffallen ;-)

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 18:55
von mcdaniels
Hallo,
ich hab das nun nochmals komplett umgebaut. Nun gibt es (nach meinem Verständnis) keine globalen Variablen mehr, denn ich rufe die jeweiligen Funktionen nur noch innerhalb der Funktion auf. Allerdings habe ich noch immer Probleme beim in die DB schreiben. Mein Insert Into mag mich nicht, wobei ich ja an sich jetzt ein Tupel übergebe. Ich bekomme allerdings diesmal auch keine Fehlermeldung. Es scheint zu funktionieren, aber in der DB landet nichts.

!!!Grade aufgefallen, mir fehlt ja der Cursor!!! -- sollte vielleicht mal ne Pause machen, seh schon den Wald vor lauter Bäumen nicht mehr :)


Hier mal mein Quelltext:

Code: Alles auswählen

import sqlite3
def db_connect():
    return sqlite3.connect("c:\guestbook.db")


def db_create():
    db_action = db_connect()
    db_action.execute('''CREATE TABLE if not exists guestbook
                         (name text, kommentar text)''')
    db_action.close()


def db_add_entry():
    entries = (raw_input("Name: "), raw_input("Kommentar: "))
    db_action = db_connect()
    db_action.execute('INSERT INTO guestbook VALUES(?,?)',entries)
    db_action.commit
    
    

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 19:12
von deets
Ein bisschen besser. Aber

- deinem commit fehlen die Klammern
- man erzeugt eine Connection *EINMAL*, und dann uebergibst du die als Parameter

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Dienstag 5. Juni 2012, 19:26
von mcdaniels
Hello,
danke - jetzt klappts... obwohl mir eigentlich der Cursor fehlt...?! (sollte wohl wie gesagt ne Pause machen)


LG
Daniel

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Mittwoch 6. Juni 2012, 06:34
von mcdaniels
Nächste Version. Jetzt gibts noch ein Problem mit dem Encoding. Es wird wohl Unicode erwartet. Sende ich nun zb nun einen Umlaut an die DB wirft mit Python einen Programming Error. Ich möge die App doch bitte zu Unicode switchen. (Recherche läuft ... ;) )

Code: Alles auswählen

import sqlite3
def db_connect(db_path):
    return sqlite3.connect(db_path)


def db_create():
    db_action = db_connection.cursor()
    db_action.execute('''CREATE TABLE if not exists guestbook
                         (name text, kommentar text)''')
    db_action.close()


def db_add_entry():
    db_action = db_connection.cursor()
    db_action.execute('INSERT INTO guestbook VALUES(?,?)',
                      (raw_input("Name: "), raw_input("Kommentar: ")))
    db_connection.commit()
    db_connection.close()

db_connection = db_connect("c:/guestbook.db")

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Mittwoch 6. Juni 2012, 06:59
von gkuhl
Hi mcdaniels,

habe deine Funktionen mal ausprobiert und es funktioniert doch eher suboptimal (ich hoffe du bist mit IPython vertraut):

Code: Alles auswählen

In [2]: conn = db_connect(':memory:')

In [3]: db_add_entry()
Name: bla
Kommentar: blub
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
/home/gerrit/<ipython-input-3-2f82cd18e3da> in <module>()
----> 1 db_add_entry()

/home/gerrit/<string> in db_add_entry()

NameError: global name 'db_connection' is not defined

In [4]: db_connection = db_connect(':memory:')

In [5]: db_add_entry()
Name: bla
Kommentar: blub
---------------------------------------------------------------------------
OperationalError                          Traceback (most recent call last)
/home/gerrit/<ipython-input-5-2f82cd18e3da> in <module>()
----> 1 db_add_entry()

/home/gerrit/<string> in db_add_entry()

OperationalError: no such table: guestbook

In [6]: db_create()

In [7]: db_add_entry()
Name: bla
Kommentar: blub

In [8]: db_add_entry()
Name: foo
Kommentar: flub
---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)
/home/gerrit/<ipython-input-8-2f82cd18e3da> in <module>()
----> 1 db_add_entry()

/home/gerrit/<string> in db_add_entry()

ProgrammingError: Cannot operate on a closed database.
Wie hier schon mehrmals erwähnt solltest du das "Connections Objekt" als Parameter übergeben. Auch kannst du die Datenbank auch nicht einfach in einer Funktion schließen, da das zu ungewollten Seiteneffekten führt. Hier mal deine Funktion etwas abgewandelt:

Code: Alles auswählen

def db_add_entry(conn):
    db_action = conn.cursor()
    db_action.execute('INSERT INTO guestbook VALUES(?,?)', (raw_input("Name: "), raw_input("Kommentar: ")))
    conn.commit()
Grüße
Gerrit

Re: SQlite3 - Verbindungsobjekt bzw. Cursor in Funktion

Verfasst: Mittwoch 6. Juni 2012, 07:51
von mcdaniels
Hallo,
nein IPython hab ich selbst noch nicht angewendet.

Betreffend das Connection Objekt als Parameter übergeben, sollte das doch so aussehen oder:

Code: Alles auswählen

def db_add_entry(db_connection):
    db_action = db_connection.cursor()
    db_action.execute('INSERT INTO guestbook VALUES(?,?)',
                      (raw_input("Name: "), raw_input("Kommentar: ")))
    db_connection.commit()
    

db_connection = db_connect("c:/guestbook.db")
db_connection = <sqlite3.Connection object at 0x0206F0B0>

Dann muss ich die Funktion aber so aufrufen:

Code: Alles auswählen

db_add_entry(db_connection)
damit es funktioniert.

@gkuhl: Kann es sein, dass es bei deinem Versuch daran scheitert, dass bei dir das Connection-Objekt nicht db_connection heisst, sondern nur conn du dann aber mein Programm verwendest in dem auf "db_connection" zugegriffen wird?

Könnte man den Cursor auch ausserhalb der Funktionen als globale Variable erstellen, oder gilt: Je weniger globale Variablen, desto besser?




LG
Daniel