sqlite query result in wx.TextCtrl ausgeben

Plattformunabhängige GUIs mit wxWidgets.
Antworten
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Hi zusammen,

ich habe eine query, die mir z. B. anhand einer Bankleitzahl die Adresse darstellen soll. Per fetchall() bekomme ich auch das, was ich abfrage:
Bank Musterhaus
Musterstraße 1
12345 Musterhausen
In einem wx.Frame habe ich jetzt eine TextCtrl erstellt, die mir das anzeigen soll, was ich bereits per fetchall() in der Shell angezeigt bekomme.

Sowas wie self.m_text1.SetValue(cur.fetchall()) funktioniert nicht. Da bekomme ich nur:

Code: Alles auswählen

TypeError: TextEntry.SetValue(): argument 1 has unexpected type 'list'
Könnt ihr mir helfen, dass ich meine Query-Result in meine TextCtrl dargestellt bekomme?

btw: Kann auch ein anderes Boxelement sein, solange man den Inhalt dann noch markieren und z. B. in die Zwischenablage legen kann.

Und hier noch meine snippes:

Code: Alles auswählen

self.m_text1 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.Point( 5,5 ), wx.DefaultSize, 0 )
self.m_text1.SetMinSize( wx.Size( 500,150 ) )
bSizer1.Add( self.m_text1, 0, wx.ALL, 5 )
snip nur für Bankname als Beispiel:

Code: Alles auswählen

def blzRQ( self, event ):
                blz_rq = self.m_searchCtrl1.GetValue()
                try:
                    sql_cmd = """
                    Select Name \
                    from tbl_ibb where Blz=?"""

                    cur.execute(sql_cmd, (blz_rq,))
                    con.commit()
                    print(cur.fetchall())
                    
                    self.m_text1.SetValue(cur.fetchall())
                    
                    
                except NameError:
                    self.error_mess.SetLabel('Keinen Eintrag gefunden!')
Vielen Dank und Grüße
Jake
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Fehlermeldung sagt es doch: du versuchst eine Liste als String zu verwenden. Das geht natuerlich nicht. Du musst die List schon wandeln in eine Textdarstellung, die richtig ist fuer dich. ZB mit "\n".join(daten) zu mehreren Zeilen untereinander. Oder ggf. mit Formatierungen (ich kenne mich mit wx nicht aus), falls du Dinge wie Fettdruck etc brauchst.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@jake012: zu Deinen snippets: Der Backslash im String ist überflüssig und gehört weg. Backslashes sollte man generell versuchen zu vermeiden. Dein Tabellenname tbl_ibb ist sehr schlecht, weil dass in Datenbanken Tabellen sind, ist irgendwie klar und ibb ist eine kryptische Abkürzung, die nur unnötige Denkarbeit benötigt, um sie zu entschlüsseln. Wenn einmal fetchall aufgerufen wurde, sind alle Daten gelesen, ein weiteres fetchall führt zu einer leeren Liste. `cur` und `con` sind nicht definiert und führen zu einem NameError. `NameError`s sind immer Programmierfehler, es macht also nie Sinn, diese Exception abzufangen. In dem von Dir vermuteten Zusammenhang tritt in diesem Block ein NameError auch nie auf.
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Dank, das war mir nicht klar... Aber nun hab ich es halbwegs hinbekommen, dass es nun dargestellt wird.

Bringt mir aber nicht das gewünschte Ergebnis in dieser Form:
Bankname
Adresse
Ort PLZ
Sondern so wird es mir dargestellt:
'Bank Musterbank', 'Musterstraße 1', 12345 Musterhausen'
Habe schon versucht, den Code list1= "\n".join(map(str,a))[1:-1] dementsprechend zu bearbeiten, komme aber leider nicht ans Ziel.

Code: Alles auswählen

def blzRQ( self, event ):
                blz_rq = self.m_searchCtrl1.GetValue()
                try:
                    sql_cmd = """
                    Select Name, case when Postfach !="" then 'Postfach '|| Postfach when Strasse !="" then Strasse end AS Adresse,PLZ || ' ' || Ort 
                    from tbl_ibb where Blz=?"""

                    cur.execute(sql_cmd, (blz_rq,))
                    con.commit()
                    a=cur.fetchall()

                    list1= "\n".join(map(str,a))[1:-1]
                    print(list1)
                    
                    self.m_text1.SetValue(list1)
                    
                    
                except NameError:
                    self.error_mess.SetLabel('Keinen Eintrag gefunden!')
@Sirius3: Danke für dein Feedback.
con = sqlite3.connect("./lib/db/datenbank.db")
cur = con.cursor()

Errors hatte ich bis auf das Genannte nicht. Jetzt gar keine mehr, sondern nur noch 'Kosmetik', die es zu bewältigen geht...
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@jake012: ich würde solche Stringverarbeitung nicht in SQL sondern in Python programmieren. Das except ist immer noch da. String-Repräsentationen von Listen ist nur für eine Debugausgabe gedacht, nicht um das produktiv einzusetzen. Zum Formatieren der einzelnen Datenbankeinträge gibt es format:

Code: Alles auswählen

name, strasse, stadt = entry
text = "{}\n{}\n{}".format(name, strasse, stadt)
bzw. besser:

Code: Alles auswählen

name, strasse, postfach, plz, stadt = entry
adresse = 'Postfach {}'.format(postfach) if postfach else strasse
text = "{}\n{}\n{} {}".format(name, adresse, plz, stadt)
Wie viele Ergebnisse erwartest Du eigentlich von Deiner SQL-Abfrage? Wenn es nämlich nur eine ist, dann ist fetchall die falsche Funktion.
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Sirius3 hat geschrieben:Zum Formatieren der einzelnen Datenbankeinträge gibt es format: ...

Wie viele Ergebnisse erwartest Du eigentlich von Deiner SQL-Abfrage? Wenn es nämlich nur eine ist, dann ist fetchall die falsche Funktion.

Oh je, ich weiß gar nicht, wie ich querries in python bearbeite... Gibt es dazu ein Stichwort, was ich mir anlernen kann? Dein Auszug zeigt mir jetzt nicht, wie ich verknüpfen muss...

Also als Ergebnis gibt es 1:1 match mit dem Eintrag in der DB. Also ich suche nach der BLZ 11111111 und in der Textbox soll Name, Straße oder Postfach und PLZ + Ort zu der Bank mit der BLZ 11111111 stehen. Jede BLZ gibt es nur 1x in der Tabelle. Also fetch() gibt mir nur ein Wert aus der Zeile, deshalb wäre fetchall() für
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Habe das jetzt folgendermaßen gelöst bekommen:

Code: Alles auswählen

con = sqlite3.connect("Datenbank.db")
cur = con.cursor()
def blzRQ( self, event ):
                blz_rq = self.m_searchCtrl1.GetValue()
                self.error_mess.SetLabel('')
                self.m_text1.SetValue('')
                try:
                    sql_cmd = """
                    Select Name,case when Postfach !="" then 'Postfach '|| Postfach when Strasse !="" then 
                    Strasse end AS Adresse, PLZ || ' ' || Ort
                    from tbl_ibb where Blz=?"""
                    cur.execute(sql_cmd, (blz_rq,))
                    a=cur.fetchone()
                    con.commit()
                    list1= "\n".join(map(str,a))
                    self.m_text1.SetValue(list1)
                except TypeError:
                    self.error_mess.SetLabel('Keinen Eintrag gefunden!')
fetchone() und fetchall() machten mir Probleme... Zudem hatte ich das zwingend erforderliche wx.TE_MULTILINE in meiner wx.TextCtrl nicht aktiv und somit hätte es niemals ein Umbruch machen können.

@Sirius3: Die Sache mit den Datenbankeinträgen formatieren bringt mich aber noch ins Grübeln... du sagtest ja, meine Methode nutzt man eher für debugging und formatiert direkt in Python... Da verstehe ich noch nicht, wie das funktioniert.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@jake012: die Lösung hab ich Dir doch schon geschrieben.
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

@Sirius3: Dann verstehe ich die Lösung nur nicht... Hmm, schon beim name,strasse,postfach,ort = entry sagt der mir, dass der Name entry nicht definiert ist. Womit soll ich das definieren?

Sieht jetzt so aus:

Code: Alles auswählen

con = sqlite3.connect("Datenbank.db")
cur = con.cursor()


def blzRQ( self, event ):
                try:
                sql_cmd = """
                    Select Name,Postfach,Strasse,PLZ,Ort,Faxnummer
                    from tbl_ibb where Blz=?"""

                    Name, Strasse, Postfach, PLZ, Ort, Faxnummer = entry
                    Adresse = 'Postfach {}'.format(postfach) if Postfach else Strasse
                    text = "{}\n{}\n{} {}".format(Name, Adresse, PLZ, Ort)

                    
                    cur.execute(sql_cmd, (blz_rq,))

                    con.commit()
                 
                    self.m_text1.SetValue(text)
                                        
                except TypeError:
                    self.error_mess.SetLabel('Keinen Eintrag gefunden!')
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@jake012: Du weißt, was `entry` bedeutet? (Datenbankeintrag)

Statt einen TypeError abzufangen solltest Du prüfen, ob das Ergebnis von fetchone `a` (ein furchtbar schlechter Name für einen Datenbankeintrag) None ist oder nicht. `list1` ist auch ein schlechter Name, weil es keine Liste ist und die 1 eine willkürliche Nummer ist. `text` ist zwar auch ziemlich generisch, aber immerhin nicht falsch. `commit` ist hier unnütz, wenn nicht gar gefährlich. Was willst Du denn bei einer Abfrage bestätigen?
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

@Sirius3: Es funktioniert jetzt :D Vielen Dank für die Denkanstöße und Erklärungen.

Ich habe entry tatsächlich nicht mit der Datenbank in Verbindung gebracht... Konnte meine Abfrage nun auch direkt erweitern und sieht jetzt so aus:

Code: Alles auswählen

con = sqlite3.connect("Datenbank.db")
cur = con.cursor()
def blzRQ( self, event ):

                sql_cmd = """
                Select Name,Postfach,Strasse,PLZ,Ort,Faxnummer
                from tbl_ibb where Blz=?"""
                
                cur.execute(sql_cmd, (blz_rq,))
                blz_output=cur.fetchone()

                if blz_output is not None:

                    Name,Postfach,Strasse,PLZ,Ort,Faxnummer = blz_output
                    Adresse = 'Postfach {}'.format(Postfach) if Postfach else Strasse
                    Anschrift = "{}\n{}\n{} {}".format(Name, Adresse, PLZ, Ort)
                    Fax = Faxnummer

                    self.m_Addr.SetValue(Anschrift)
                    self.m_Fax.SetValue(Fax)
                    
                else:
                    self.error_mess.SetLabel('Keinen Eintrag gefunden!')
btw: Kann man das auch als gelöst markieren?
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Hmm, eine Frage habe ich doch noch...

Es könnte in einer ähnlichen Abfrage vorkommen, dass es mehrere Zeilen in der Tabelle trifft. Wie schaffe ich es jetzt, dass nicht nur eine Zeile, sondern alle Zeilen ausgegeben werden?

Angenommen ich suche nach einer Faxnummer. Diese sind aber 2 Banken zugeordnet. Ich möchte dann auch beide Adressen untereinander ausgegeben haben wie:

Bankname A
Adresse

Bankname B
Adresse

Wenn ich print(cur.fetchone()) ausführe, werden die auch alle angezeigt. Demnach muss es ja wieder ein Format-Thema sein, oder?

Code: Alles auswählen

 
                    BLZ,Name,Postfach,Strasse,PLZ,Ort,Faxnummer = name_output
                    Adresse = 'Postfach {}'.format(Postfach) if Postfach else Strasse
                    Anschrift ="{}\n{}\t{} {}\tFax: {}\n".format(Name, Adresse, PLZ, Ort, Faxnummer)

                    self.m_Addr.SetValue(Anschrift)
Schade, dass es jetzt nicht einfach ein SetValues gibt... das wäre ja auch zu einfach
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@jake012: dazu braucht man dann eine Schleife.
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Habe das jetzt auch geschafft :)

Vollständiger halber auch hier mein Ergebnis:

Code: Alles auswählen

def nameRQ( self, event ):
                if self.BanknameS.IsEmpty():
                    self.error_mess.SetLabel('Bitte trage einen Namen ein!')
                else:
                    self.m_searchCtrl1.SetValue('')
                    name_rq = self.BanknameS.GetValue()
                    self.error_mess.SetLabel('')
                    self.m_Addr.SetValue('')
                    self.m_Fax.SetValue('')
                    

                    sql_cmd = """
                    Select blz,Name,Postfach,Strasse,PLZ,Ort,Faxnummer
                    from tbl_ibb where Name like ?"""
                    
                    cur.execute(sql_cmd, ('%'+name_rq+'%',))

                    for name_output in cur:
                        if name_output is not None:

                            BLZ,Name,Postfach,Strasse,PLZ,Ort,Faxnummer = name_output
                            Adresse = 'Postfach {}'.format(Postfach) if Postfach else Strasse
                            Fax = 'Fax: {}'.format(Faxnummer) if Faxnummer else ''
                            Anschrift = "{}\t{}\n{}\t{} {}\t{}\n\n".format(BLZ,Name, Adresse, PLZ, Ort,Fax)
                            

                            self.m_Addr.AppendText(Anschrift)
                            
                            print(Anschrift)

                            self.m_Addr.SetFocus()

                        else:
                            self.error_mess.SetLabel('Keinen Eintrag gefunden!')
Nur scheinbar funktioniert mein else nicht richtig... Wenn ich nichts im Suchfeld stehen habe, bekomme ich meine Fehlermeldung (alse else wird ausgeführt). Wenn ich aber keinen Treffer habe -- es steht was im Suchfeld, kann aber nicht gefunden werden -- wird meine Fehlermeldung (also else wird nicht ausgeführt) nicht angezeigt.

Ist, wenn im Suchfenster nichts steht, None nicht mehr gegeben? Ich hab auch noch keine Idee, wie ich das fixen könnte... Aber das wichtige, der loop funktioniert jedenfalls :)
Antworten