gui - datenbank (tabelle)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Gast

hallo tabellar,

Also müssen wir auf das Erzeugen der individuellen Datenbanktabellen (MySQL, PostgreSQL, Gadfly, etc. mit autoincrement, serial usw.) hier gar nicht eingehen.
die sache mit auto_increment ist nicht nur für das erstellen von tabellen wichtig:

in meinem code gibt es eine regel (noch als einfach if-anweisung):
datenfelder die eben auto_increment aufweisen werden ignoriert beim anzeigen, beim erstellen der eingabefelder, der radiobuttons.
natürlich nicht beim schrieben von daten(!)

da postgresql das nicht kennt und ich in einer allfälligen api für versch. sqldbs mich auf einen gemeinsamen nenner einigen müsste, müsste der user drauf aufmerksam gemacht werden, dass er mysql, die auto_increment verwendet, vergessen kann.

freilich liessen sich alle spezfika einer db berücksichtigen, aber irgendwie riecht mir das nach frickeln. im sinne von elendslangen if anweisungen usw.

und wenn man da loslegt, finden sich sicherlich noch viele "auto_increments", deshalb war meine idee gleich für jede db eine eigene api, auch wenn sich viele sqlanweisungen möglicherweise wiederholen.

ich habe deinen thread in allgemeine fragen gelesen bez. "show columns..."
da ist es also schon das zweite auto_increment:d

mfg

rolgal



[/quote]
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Hi Rolgal,
...in meinem code gibt es eine regel (noch als einfach if-anweisung):
datenfelder die eben auto_increment aufweisen werden ignoriert beim anzeigen, beim erstellen der eingabefelder, der radiobuttons.
natürlich nicht beim schrieben von daten(!)...
Das habe ich schon erkannt ;-) , letztendlich braucht Deine Hauptfensterklasse (HF-Klasse) ja nur die Zuweisung von "self.cols_names" bei der Instantiierung durch die Angabe einer "ColumnsList".

Diese ColumnsList erhält die HF-Klasse durch eine Anfrage bei der TableWrapper Class. Z.B. so:

>>> self.col_names=self.tablewrapper.getColumnsAsList(tablename)

Die TableWrapper Class agiert sozusagen dann als API oder "Gate" Klasse, die wiederum die individuellen DB APIs beinhaltet.
Solche APIs wären dann zb. MySQL.py, PgSQL.py, XMLdb.py, die alle die Methode "getColumnsAsList" via "import" zur Verfügung stellen mit Übergabe der entsprechenden ColumnsList, OHNE Primärschlüssel (autoincrement, serial, etc.).

Super wäre also so etwas in der Art:

HF-Class
TableWrapper Class
MySQL.py API
PgSQL.py API
XMLdb.py API

Ich werde mich im nächsten Schritt mal um die PgSQL und XML Geschichte kümmern.

Gruss Tabellar
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Ich hab hier jetzt mal einen ersten Entwurf für den GUI Datenbank Client von rolgal. Das ganze ist natürlich noch nicht fertig, aber die Trennung von GUI und der Datenbank ist erfolgt. Im Moment kann ich auf PostgreSQL und XML Datenbanken (db.xml) zugreifen (Daten anzeigen und Daten neu einfügen) .

Das db-GUI greift über das TableWrapper Modul und den entsprechenden DB-DAOs Modulen (pgsqlDAO, xmlDAO - DAO=Data Access Objects) auf die Daten zu. Auf die XML Datenbankdatei wird über das interne Python Modul "MiniDOM" zugegriffen. Der dbGUI Client kann somit allein mit Python Bordmitteln genutzt werden. Ein DB-Server muss nicht installiert sein.

Durch die Trennung des GUIs von dem DAO Modul, kann jetzt die Funktionalität des GUIs einfacher verbessert werden.

Mögliche weitere Schritte sind:

GUI:
- Datenbankauswahl via Dialogfenster
- Tabellenauswahl
- Schönere Datenansicht

DBDAO:
- Implementation des mySQL DAO Moduls (ich kann nicht auf meinen Gentoo MySQL Server von extern zugreifen...)
- Implementation sqliteDAO
- Bearbeiten und Löschen Funktion

Viel Spass
Tabellar


Bild

Db GUI Client

Code: Alles auswählen

#!/usr/bin/env python
#Modul uniDbGUI - Universaler Datenbank GUI Client

from Tkinter import *
import sys
import tkMessageBox
import tableWrapper

class Fenster:


    #Abstand der Elemente
    a_x=5 #Abstand horizontal
    a_y=5 #Abstand vertikal

    #Die Buttons bekommen alle dieselbe Breite
    b=20

    #Farbe wird auch von la verwendet
    f="#00ff00"

    lt=""

   
    def __init__(self):

        raise NotImplementedError("Abstract class")

   

    def prepare(self):

        self.root.resizable(0,0)

        self.root.protocol("WM_DELETE_WINDOW",self.mainexit)

        la=Label(self.root,bg=self.f,text="///GARO_GUI")
        la.grid(row=0,column=0,           
                     columnspan=2,
                     padx=self.a_x,pady=self.a_y)
     
        la_1=Label(self.root,bg=self.f,text=self.lt)
        la_1.grid(row=1,column=0,
                       columnspan=2,
                       padx=self.a_x,pady=self.a_y)
       
        button=Button(self.root,
                           text="Prog. Beenden",
                           width=10,command=self.ende)
        button.grid(row=18,column=0,
                         columnspan=2,
                         padx=10,pady=10)



    def ende(self):

        antwort=tkMessageBox.askyesno\
                 ("Warnung","Sicher beenden?")
        if antwort==1:
            sys.exit(0)


    def mainexit(self):
       
        self.root.withdraw()


class HauptFenster(Fenster):

    lt="Ausgabe"

       
    def __init__(self):
        """,host,user,db,db_table,passwd=''"""
        self.root=Tk()
        self.root.wm_geometry('+20+20')
        self.prepare()
        self.root.title("Hauptfenster")
         
        #instantiierung DbWrapper... 
        self.tablewrapper=tableWrapper.TableWrapper()

        # Manuelle Datenbankauswahl via Angabe 
        # des Datenbanktyps: (später durch Dialogauswahl...)
        # PgSQL 
        # MySQL
        # XML
        self.tablewrapper.connect('XML','host','dbname','user')


        self.cols_names=self.tablewrapper.getTableColumnsList() 
        li_1width=len(self.cols_names)*12
        self.s_cols_names=", ".join(self.cols_names)

        scb_v=Scrollbar(self.root, orient="vertical")
        scb_h=Scrollbar(self.root, orient="horizontal")

        self.li_1=Listbox(self.root,
                          width=li_1width,height=8,
                          yscrollcommand=scb_v.set,
                          xscrollcommand=scb_h.set)
       
        scb_v["command"]=self.li_1.yview
        scb_h["command"]=self.li_1.xview
       
        self.li_1.grid(row=2,column=0,
                       columnspan=2,
                       padx=self.a_x,pady=self.a_y)
       
        scb_v.grid(row=2,column=2,
                      padx=self.a_x,pady=self.a_y)
       
        scb_h.grid(row=3,column=0,
                        columnspan=2,
                        padx=self.a_x,pady=self.a_y)
                       

        b_1=Button(self.root,width=self.b,text="Alle Daten",
                        command=lambda:
                        self.list_data(self.tablewrapper.getTableColumnsData()))
                   
        b_1.grid(row=4,column=0,
                      padx=self.a_x,pady=self.a_y)
       

        b_2=Button(self.root,width=self.b,
                        text="Loeschen",command=self.ask)
        b_2.grid(row=4,column=1,
                      padx=self.a_x,pady=self.a_y)
       
       
        b_3=Button(self.root,width=self.b,text="Sortieren",
                        command=lambda:
                        self.list_data
                        (result=self.database.get_data
                        ("select %s from %s order by %s"%(self.s_cols_names,self.db_table,choice.get()))))
        b_3.grid(row=5,column=0,
                      padx=self.a_x,pady=self.a_y)
       

        b_4=Button(self.root,width=self.b,text="Eingabe",
                        command=lambda:
                        EingabeFenster
                        (self.root,self.tablewrapper,
                         self.root.winfo_width()))

       
        b_4.grid(row=5,column=1,
                      padx=self.a_x,pady=self.a_y)


        b_5=Button(self.root,width=self.b,text="Suchen",
                        command=lambda:
                        SuchFenster
                        (self,self.root,self.database,self.db_table))
        b_5.grid(row=6,column=0,
                      padx=self.a_x,pady=self.a_y)


        b_6=Button(self.root,width=self.b,text="Bearbeiten",
                        command=lambda:
                        BearbeitungsFenster
                        (self.root,self.database,
                         self.db_table,self.li_1.get("active"),
                         self.root.winfo_width()))
        b_6.grid(row=6,column=1,padx=self.a_x,pady=self.a_y)

        
    def list_data(self,result):
       
        self.li_1.delete(0,END)
        result=[list(element) for element in result]

        for element in result:
            for i in range(len(element)):
                element[i]=str(element[i])
               
        result=["   ".join(element) for element in result]

        for elements in result:
                self.li_1.insert("end",elements)
        self.root.title("Hauptfenster-Connected...")

        #radio buttons...
        choice=StringVar()
        for i in range(len(self.cols_names)):

            rb=Radiobutton(self.root,text=self.cols_names[i],
                        value=self.cols_names[i],
                        variable=choice)
            rb.grid(row=i+7,column=1,
                         padx=self.a_x,pady=self.a_y)

            if i==0:
                rb.select()



    def get_selected(self,action):
        """muss noch gemacht werden"""

   

    def ask(self):
        """muss noch gemacht werden"""


class EingabeFenster(Fenster):

    lt="Eingabe/Bearbeitung"

   
    def __init__(self,root,tablewrapper,hfwidth):

        self.root=Toplevel(root)
        self.root.wm_geometry('%s+40'%('+'+str(hfwidth+80)))
        self.root.title("Eingabefenster")
        self.prepare()
        self.tablewrapper=tablewrapper
        self.cols_names=self.tablewrapper.getTableColumnsList() 
        self.s_cols_names=", ".join(self.cols_names)
        self.entrylist=[]

        for i in range(len(self.cols_names)):
            label=Label(self.root,text=self.cols_names[i])
            label.grid(row=i+2,column=0)

            entry=Entry(self.root)
            entry.grid(row=i+2,column=1,padx=self.a_x,pady=self.a_y)
            self.entrylist.append(entry)

        b_1=Button(self.root,text="Eintragen",command=self.printEntries)
        b_1.grid(row=(len(self.cols_names)+2),column=2,padx=self.a_x,pady=self.a_y)
        
    def printEntries(self):
        """---"""
        entryTextList=[]
        for element in self.entrylist:
            entryTextList.append(element.get())

        for element in self.entrylist:
            element.delete(0,"end")

        print self.cols_names
        print entryTextList
        self.tablewrapper.insertTableColumnsData(self.cols_names,entryTextList)

        
if __name__=="__main__":
   hf = HauptFenster()
   mainloop() 
tableWrapper Modul:

Code: Alles auswählen

#!/usr/bin/env python
#Modul tableWrapper

import pgsqlDAO
import mysqlDAO
import xmlDAO


class TableWrapper:
    """dbwrapper"""
    def __init__(self):
        """init"""

    def connect(self,DbType,Host,Db,User):

        if DbType=='PgSQL':
           self.dbWrapper=pgsqlDAO.PgSqlDAO()
           self.dbWrapper.connect(Host,Db,User)

        if DbType=='MySQL':
           self.dbWrapper=mysqlDAO.MySqlDAO()
           self.dbWrapper.connect(Host,Db,User)

        if DbType=='XML':
           self.dbWrapper=xmlDAO.XmlDAO()
           
    def getTableNameList(self):
        return self.dbWrapper.getTableNameList()

    def getTableColumnsList(self):
        return self.dbWrapper.getTableColumnsList()
    
    def getTableColumnsData(self):
        return self.dbWrapper.getTableColumnsData()

    def insertTableColumnsData(self,colList,valueList):
        self.dbWrapper.insertTableColumnsData(colList,valueList)
        
    def close(self):
        self.dbWrapper.close()
pgsqlDAO Modul:

Code: Alles auswählen

#!/usr/bin/env python
#Modul pgsqlDAO

#PostgreSQL WIN dbAPI
import pyPgSQL.PgSQL

class PgSqlDAO:
    def __init__(self):
        """..."""
        
    def connect(self,Host,Db,User):
        self.con=pyPgSQL.PgSQL.connect(host=Host,database=Db,user=User)            
        

    def getTableNameList(self):
        cur=self.con.cursor()
        sql="""SELECT tablename
               FROM pg_tables
               WHERE tablename !~* 'pg_*' and tablename !~* 'sql_*';"""
        cur.execute(sql)
        pgresult=cur.fetchall()
        tableNameList=[]
        for i in pgresult:
            tableNameList.append(i[0])
                
        return tableNameList

    def getTableColumnsList(self):
        cur=self.con.cursor()
        sql="""SELECT a.attnum, a.attname AS field, t.typname AS type,
               a.attlen AS length, a.atttypmod AS length_var,
               a.attnotnull AS not_null, a.atthasdef as has_default
               FROM pg_class c, pg_attribute a, pg_type t
               WHERE c.relname = 'tfoo'
               AND a.attnum > 0
               AND a.attrelid = c.oid
               AND a.atttypid = t.oid
               ORDER BY a.attnum;"""
        cur.execute(sql)
        pgresult=cur.fetchall()
        tableColList=[]
        for i in pgresult:
            tableColList.append(i[1])
        return tableColList

    def getTableColumnsData(self):
        cur=self.con.cursor()
        sql="""SELECT * from tfoo;"""
        cur.execute(sql)
        pgresult=cur.fetchall()
               
        return pgresult

    def insertTableColumnsData(self,colList,valueList):
        cur=self.con.cursor()        
        columns = colList
        cols = ", ".join(columns)
        value_placeholders = ", ".join(["%s"] * len(columns))
        values = valueList
        sql = """insert into tfoo (%(cols)s)
                 values (%(value_placeholders)s)""" % locals()
        cur.execute(sql, values)
        self.con.commit()

    def close():
        self.con.close()

xmlDAO Modul:

Code: Alles auswählen

#!/usr/bin/env python
#Modul xmlDAO

#Pythoninternes xmlAPI
import xml.dom.minidom

class XmlDAO:
    def __init__(self):
        """ --- """
        self.filename="db.xml"
        self.doc = xml.dom.minidom.parse(self.filename)              

        tables=[]
        mapping = {}

    def getTableNameList(self):
        tableNameList=[]
        for i in self.doc.getElementsByTagName("table"):
            name=i.getAttribute("name")
            tableNameList.append(name)
        return tableNameList

    def getTableColumnsList(self):
        for table in self.doc.getElementsByTagName("table"):
            if table.getAttribute("name")=='t1':
               rows=table.getElementsByTagName("row")
               if len(rows):
                  colList=[]
                  for col in rows[0].childNodes:
                      if col.nodeType==col.ELEMENT_NODE:
                         colList.append(col.tagName)
        return colList 


    def getTableColumnsData(self):  
        for table in self.doc.getElementsByTagName("table"):
            if table.getAttribute("name")=='t1':
               rows=table.getElementsByTagName("row")
               colList=self.getTableColumnsList()
               datalist=[]

               for i in range(len(rows)):
                   list=[]
                   datalist.append(list)

               rowid=-1
               for row in rows:
                   rowid=rowid+1

                   for name in colList:
                       for item in row.getElementsByTagName(name):
                           item.normalize()
                           datalist[rowid].append(item.firstChild.data)

        return datalist


    def insertTableColumnsData(self,colList,valueList):

        xmlstr='<row>'
        for i in range(len(colList)):
            xmlstr = xmlstr + "<" + colList[i] + ">" + valueList[i] + "</" + colList[i] + ">"
        rowxml = xmlstr + "</row>\n"

        rowMiniDOM=xml.dom.minidom.parseString(rowxml)
        rowDOM=rowMiniDOM.documentElement                        

        for table in self.doc.getElementsByTagName("table"):
            if table.getAttribute("name")=='t1':
               table.appendChild(rowDOM)

        #xml_save
        xmlfile=open(self.filename,'w')
        xmlfile.write(self.doc.toxml())
        xmlfile.close()
XML Datenbankdatei db.xml :

Code: Alles auswählen

<?xml version="1.0" ?>
<xmldb>

  <table name="t1">
    <row><id>101</id><username>rolgal</username><usertype>Poweruser</usertype></row>
    <row><id>102</id><username>Leonidas</username><usertype>Poweruser</usertype></row>
    <row><id>103</id><username>Olliminatore</username><usertype>User</usertype></row>
    <row><id>104</id><username>tabellar</username><usertype>User</usertype></row>   
  </table>

  <table name="t2">
    <row><titel>Python und XML</titel><author>xyz</author></row>
  </table>

 </xmldb>
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Somit hast du die GUI von der Datenbank entkoppelt.. also man könnte auch andere GUIs machen. Nett :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Gast

hallo tabellar,

sieht ja echt gut aus,....bin platt :D

ich krieg leider keine benachrichtigungen, wenn zu diesem thread antworten geschrieben werden, war also glücklicher zufall, dein post gesehen zu haben, keine ahnung was da los ist.

grüße

rolgal
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

rolgal hat geschrieben:hallo tabellar,
sieht ja echt gut aus,....bin platt :D
Schön, wenn es Dir gefällt. Hättest Du nicht Lust die "mysqlDAO" zu machen? Ich habe leider keinen Zugriff von meinem WIN Notey auf meinen Gentoo Linux Rechner, auf dem MySQL aber wunderbar läuft. Auch Rechte setzen bringt nichts, hm, keine Ahnung warum der connect auf den MySQL Server von externen Rechnern nicht funktioniert :cry: .

Gruss Tabellar
Gast

hi tabellar,

ich werde gern das mysqlDAO machen,

bis bald oder auch etwas länger wenn es fuchst :D

rolgal
Gast

hallo tabellar,


also ich habe mich soweit ich konnte mit deinem code auseinandergesetzt und mir ist da einiges aufgefallen, was wir besprechen sollten,....

1. tablewrapper: import anweisungen würde ich hier machen:


Code: Alles auswählen

        if DbType=='MySQL':
           import mysqlDAO
           self.dbWrapper=mysqlDAO.MySqlDAO()
           self.dbWrapper.connect(Host,User,Db,Passwd)
es ist nicht so sinnvoll alle schnittstellen automatisch zu importieren.


2. db_client, es braucht meiner meinung nach auf jeden fall auch die ermitllung der aktuellen tabelle, sonst wird es schwierig die daten auszugeben, in deinem beispiel geht das nur weil du eine konstante verwendest:

Code: Alles auswählen

    def getTableColumnsData(self):
        cur=self.con.cursor()
        sql="""SELECT * from tfoo;"""
        cur.execute(sql)
        pgresult=cur.fetchall()
tfoo ist gemeint

daher:

Code: Alles auswählen

    def __init__(self):
        """,host,user,db,db_table,passwd=''"""
        self.root=Tk()
        self.root.wm_geometry('+20+20')
        self.prepare()
        self.root.title("Hauptfenster")
         
        #instantiierung DbWrapper...
        self.tablewrapper=tableWrapper.TableWrapper()

        # Manuelle Datenbankauswahl via Angabe
        # des Datenbanktyps: (später durch Dialogauswahl...)
        # PgSQL
        # MySQL
        # XML

        self.tablewrapper.connect('MySQL','localhost','ein user','eine db','ein passwort')

        #tabellen ermitteln
        self.db_tables=self.tablewrapper.getTableNameList()
        #erste tabelle als default verwenden, später kann diese variable ueber das auswahlfenster neu gesetzt werden
        self.db_table=self.db_tables[0]

        self.cols_names=self.tablewrapper.getTableColumnsList(self.db_table)

die tabelle self.db_table muss für diverse anweisungen übergeben werden.

mein nicht fertiges mysqlDAO sieht momentan so aus:

Code: Alles auswählen

import MySQLdb
from _mysql_exceptions import *


class MySqlDAO:

    def __init__(self):
        """..."""


    def connect(self,host,user,db,passwd=''):

        self.db=db
        
        try:
            self.conn=MySQLdb.connect(host=host,
                                      user=user,
                                      db=db,
                                      passwd=passwd)
             
        except OperationalError, msg:
            print msg[1]


    def getTableNameList(self):
        cur=self.conn.cursor()
        cur.execute("show tables from %s"%self.db)
        result=cur.fetchall()
        tableNameList=[]
        for i in result:
            tableNameList.append(i[0])
        return tableNameList
    
            

    def getTableColumnsList(self,db_table):
        cur=self.conn.cursor()
        cur.execute("show columns from %s"%db_table)
        result=cur.fetchall()
        tableColList=[]
        for i in result:
            if 'auto_increment' not in i:
                tableColList.append(i[0])
        return tableColList
         
    
    def getTableColumnsData(self,action,db_table):

        action=action%db_table
        cursor=self.conn.cursor()
        cursor.execute(action)
        result=cursor.fetchall()
        cursor.close()
        return result


    def insertTableColumnsData(self,action):

        cursor=self.conn.cursor()
        cursor.execute(action)
        cursor.close()
 

    def close(self):
        
        self.conn.close()
prinzipiell arbeite ich ja auch noch an der alten fassung, die bereits tabellen wechseln kann, es scheint sinnvoll den db_client, das hauptfenster so zu gestalten:

Code: Alles auswählen

class HauptFenster(Fenster):

    lt="Ausgabe"

       
    def __init__(self,host,user,db,passwd=''):

        self.root=Tk()

        self.root.wm_geometry('+20+20')

        self.prepare()

        self.root.title("Hauptfenster")
         
        self.database=MySqlApi(host,user,db,passwd)

        if self.database.table_number>1:
            auswahl=AuswahlFenster(self,self.root,self.database.db_tables)

        
       
        self.choice=StringVar()

        self.db_table=self.database.db_table
        self.db_table_cols=self.database.db_table
        self.cols_names=self.database.cols_names

        self.s_cols_names=", ".join(self.cols_names)

        self.li_1width=len(self.cols_names)*12
        
       # das ist es um was es geht, frames, die spaeter einzeln neu gesetzt werden koennen.
        self.frame_1=Frame(self.root)
        self.frame_1.grid(row=2,column=0,columnspan=2)
        self.set_frame_1()

        self.frame_2=Frame(self.root)
        self.frame_2.grid(row=5,column=0,columnspan=2)
        self.set_frame_2()

        self.frame_3=Frame(self.root)
        self.frame_3.grid(row=9,column=1)
        self.set_frame_3()



    def set_frame_1(self):

        self.scb_v=Scrollbar(self.frame_1, orient="vertical")
        self.scb_h=Scrollbar(self.frame_1, orient="horizontal")

        self.li_1=Listbox(self.frame_1,
                          width=self.li_1width,height=8,
                          yscrollcommand=self.scb_v.set,
                          xscrollcommand=self.scb_h.set)

      
        self.scb_v["command"]=self.li_1.yview
        self.scb_h["command"]=self.li_1.xview
       
        self.li_1.grid(row=3,column=0,
                       columnspan=2,
                       padx=self.a_x,pady=self.a_y)
       
        self.scb_v.grid(row=3,column=2,
                      padx=self.a_x,pady=self.a_y)
       
        self.scb_h.grid(row=4,column=0,
                        columnspan=2,
                        padx=self.a_x,pady=self.a_y)

        
        
    def set_frame_2(self):

        b_1=Button(self.frame_2,width=self.b,text="Alle Daten",
                        command=lambda:
                        self.list_data
                        (result=self.database.get_data
                        ("select %s from %s"%(self.s_cols_names,self.db_table))))
        b_1.grid(row=6,column=0,
                      padx=self.a_x,pady=self.a_y)
       

        b_2=Button(self.frame_2,width=self.b,
                        text="Loeschen",command=self.ask)
        b_2.grid(row=6,column=1,
                      padx=self.a_x,pady=self.a_y)
       
       
        b_3=Button(self.frame_2,width=self.b,text="Sortieren",
                        command=lambda:
                        self.list_data
                        (result=self.database.get_data
                        ("select %s from %s order by %s"%(self.s_cols_names,self.db_table,self.choice.get()))))
        b_3.grid(row=7,column=0,
                      padx=self.a_x,pady=self.a_y)
       

        b_4=Button(self.frame_2,width=self.b,text="Eingabe",
                        command=lambda:
                        EingabeFenster
                        (self.frame_2,self.database,
                         self.db_table,self.cols_names,
                         self.root.winfo_width()))
        b_4.grid(row=7,column=1,
                      padx=self.a_x,pady=self.a_y)


        b_5=Button(self.frame_2,width=self.b,text="Suchen",
                        command=lambda:
                        SuchFenster
                        (self,self.root,self.database,self.db_table))
        b_5.grid(row=8,column=0,
                      padx=self.a_x,pady=self.a_y)


        b_6=Button(self.frame_2,width=self.b,text="Bearbeiten",
                        command=lambda:
                        BearbeitungsFenster
                        (self.root,self.database,
                         self.db_table,self.cols_names,
                         self.li_1.get("active"),
                         self.root.winfo_width()))
        b_6.grid(row=8,column=1,padx=self.a_x,pady=self.a_y)

        

    def set_frame_3(self):


        for i in range(len(self.cols_names)):

            self.rb=Radiobutton(self.frame_3,text=self.cols_names[i],
                        value=self.cols_names[i],
                        variable=self.choice)
            self.rb.grid(row=i+9,column=1,
                         padx=self.a_x,pady=self.a_y)
         

            if i==0:
                self.rb.select()

        self.list_data(result=self.database.get_data("select %s from %s"%(self.s_cols_names,self.db_table)))

#usw.

auch habe ich mir ueberlegt gewisse dinge vielleicht doch im konstruktor zu setzen, z.b. im tablewrapper den datenbanktyp

das mit der übergabe von attributen ist net so einfach, will man dein ziel umsetzen einen uni db client zu haben.

Code: Alles auswählen

        b_1=Button(self.root,width=self.b,text="Alle Daten",
                        command=lambda:
                        self.list_data(self.tablewrapper.getTableColumnsData(action,self.db_table))
die auszuführende anweisung (action) und die betreffende tabelle (self.db_table) müssen hier übergeben werden, aber woher nehmen???

die tabelle ist klar, aber die action, da hirne ich noch

so, das waren mal die wichtigsten überlegungen für den moment, ich mach mal weiter,

bis bald,

wollte einfach auch mal ein lebenszeichen geben,

:D

rolgal
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Hi Rolgal,
schön, dass es auch hier ein wenig weiter geht... ;-)
rolgal hat geschrieben:1. tablewrapper: import anweisungen würde ich hier machen:

Code: Alles auswählen

        if DbType=='MySQL':
           import mysqlDAO
           self.dbWrapper=mysqlDAO.MySqlDAO()
           self.dbWrapper.connect(Host,User,Db,Passwd)
es ist nicht so sinnvoll alle schnittstellen automatisch zu importieren.
Klar, das ist natürlich so besser.

rolgal hat geschrieben:2. db_client, es braucht meiner meinung nach auf jeden fall auch die ermitllung der aktuellen tabelle, sonst wird es schwierig die daten auszugeben, in deinem beispiel geht das nur weil du eine konstante verwendest:

Code: Alles auswählen

    def getTableColumnsData(self):
        cur=self.con.cursor()
        sql="""SELECT * from tfoo;"""
        cur.execute(sql)
        pgresult=cur.fetchall()
tfoo ist gemeint

daher:

Code: Alles auswählen

...
        self.db_tables=self.tablewrapper.getTableNameList()
        #erste tabelle als default verwenden, später kann diese variable ueber das auswahlfenster neu gesetzt werden
        self.db_table=self.db_tables[0]
---
die tabelle self.db_table muss für diverse anweisungen übergeben werden.
In der TableWrapper Klasse war die Methode "getTableNameList()" ja schon vorgesehen und implementiert. Ich hab den Tabellennamen einfach fix verdrahtet, weil der Auswahldialog dazu noch nicht da war. Die Lösung mit "self.db_tables[0] ist natürlich gleich viel eleganter flexibler.
rolgal hat geschrieben:mein nicht fertiges mysqlDAO sieht momentan so aus:

Code: Alles auswählen

import MySQLdb
from _mysql_exceptions import *


class MySqlDAO:

    def __init__(self):
        """..."""
    def connect(self,host,user,db,passwd=''):
...
Super, wenn die MySqlDAO bald fertig ist. Dann ist eine reine GUI Entwicklung möglich... Hast Du vielleicht schon einen Screenshot von deiner neuen GUI (Tabellenauswahl) ?

GUI Ziele:
- DB Auswahl
- Connect
- Tabellenauswahl
- Ansichtsfenster in Tabellenform

rolgal hat geschrieben:so, das waren mal die wichtigsten überlegungen für den moment, ich mach mal weiter, bis bald, wollte einfach auch mal ein lebenszeichen geben, :D
Schön, mich freut es, wenn es hier so ein paar Projekte gibt... Meine Augen sind auf jeden Fall auch in Deinem Projekt ... :wink:

Gruss Tabellar
Gast

für folgendes problem nachstehender lösungsvorschlag, müsste man aber dann in allen DAOs so machen
das mit der übergabe von attributen ist net so einfach, will man dein ziel umsetzen einen uni db client zu haben.

Code: Alles auswählen


        b_1=Button(self.root,width=self.b,text="Alle Daten",
                        command=lambda:
                        self.list_data(self.tablewrapper.getTableColumnsData(action,self.db_table))

die auszuführende anweisung (action) und die betreffende tabelle (self.db_table) müssen hier übergeben werden, aber woher nehmen???

im konstruktor des mysqlDAO folgendes dictionary:


Code: Alles auswählen

    def __init__(self):

        self.actions={"all":"select * from %s","sort":"select %s from %s order by %s"}

die methode getTableColumnsData:

Code: Alles auswählen

    def getTableColumnsData(self,a_type,*values):

        action=self.actions[a_type]
        for element in values:
            action=action%element
            
        cursor=self.conn.cursor()
        cursor.execute(action)
        result=cursor.fetchall()
        cursor.close()
        return result
im db_client wären also die folgende änderungen notwendig:

Code: Alles auswählen

        b_1=Button(self.root,width=self.b,text="Alle Daten",
                   command=lambda:
                   self.list_data(self.tablewrapper.getTableColumnsData("all",(self.db_table))))
      
        b_1.grid(row=4,column=0,
                      padx=self.a_x,pady=self.a_y)
       
       #...b_2 interessiert jetzt keinen
       
        b_3=Button(self.root,width=self.b,text="Sortieren",
                   command=lambda:
                   self.list_data(self.tablewrapper.getTableColumnsData("sort",(self.s_cols_names,
                                                                                self.db_table,
                                                                                self.choice.get()))))
        b_3.grid(row=5,column=0,
                      padx=self.a_x,pady=self.a_y)
funktionieren tut es :D

ausserdem brauchen wir die spaltennamen 2mal, einmal für die gui, dem frontend und einmal für dateioperationen,
ich will keine id's u.ä. in der gui, schon gar nicht wenn sie automatisch hochgezählt werden, gerade für das bearbeitungsfenster, eingabefenster ist das sonst ein problem

mfg

rolgal
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Hi Rolgal,

ich hab auch ein bisschen weiter gemacht... :

Code: Alles auswählen

class TableWrapper:
    """dbwrapper"""
    def __init__(self):
        """init"""

    def connect(self,DbType,Host,Db,User):

        if DbType=='PgSQL':
           import pgsqlDAO 
           self.dbWrapper=pgsqlDAO.PgSqlDAO()
           self.dbWrapper.connect(Host,Db,User)

        if DbType=='MySQL':
           import mysqlDAO 
           self.dbWrapper=mysqlDAO.MySqlDAO()
           self.dbWrapper.connect(Host,Db,User)

        if DbType=='XML':
           import xmlDAO
           self.dbWrapper=xmlDAO.XmlDAO()
           self.dbWrapper.connect(Host,Db,User)           
           
    def getTableNameList(self):
        return self.dbWrapper.getTableNameList()

    def getTableColumnsList(self,tableName):
        return self.dbWrapper.getTableColumnsList(tableName)
    
    def getTableColumnsData(self,tableName):
        return self.dbWrapper.getTableColumnsData(tableName)

    def insertTableColumnsData(self,tableName,colList,valueList):
        self.dbWrapper.insertTableColumnsData(tableName,colList,valueList)
        
    def close(self):
        self.dbWrapper.close()
Du siehst, ich habe die "import" Anweisung in die if-condition gesteckt. Bei den Methoden siehst du jetzt ausserdem, dass überall entsprechende Argumente (tableName, etc. ) übergeben werden können. Sprich, keine "fixe" Tabellenverdrahtung mehr.

Code: Alles auswählen

        #Instantiierung DbWrapper... 
        self.tablewrapper=tableWrapper.TableWrapper()

        #Auswahl der Datenbank
        self.tablewrapper.connect('XML','10.2.0.1','db.xml','userName')
        #self.tablewrapper.connect('PgSQL','10.2.0.1','dbname','useName')

        #Festlegung der Tabelle 
        self.db_tables=self.tablewrapper.getTableNameList()
        self.db_table=self.db_tables[0]
        self.cols_names=self.tablewrapper.getTableColumnsList(self.db_table)
Hier ist der code für die "Datenbank- und Tabellenauswahl" im Konstruktor der "Hauptfenster" Klasse. Die Auswahl der XML DB ist jetzt auch dynamisch (allerdings hat sich die xmlDAO auch entspr. verändert).
rolgal hat geschrieben:<snip> ... im konstruktor des mysqlDAO folgendes dictionary:

Code: Alles auswählen

    def __init__(self):

        self.actions={"all":"select * from %s","sort":"select %s from %s order by %s"}
</snip>

Hm, ich bin nicht so der Freund von einzelnen SQL Commands in Dictionarys etc. Ich finde, dass die Übersichtlichkeit im Programm verloren geht. Ich persönlich finde es schöner, wenn sich die Funktionalität in den einzelnen Methodenaufrufen wiederspiegelt. Ausserdem haben wir bei der XML DB keine SQL Commands. Also wäre hier das Pendant zu deinem SQL-Dict eine neue Methode in der TableWrapper Class:

Code: Alles auswählen

def getTableColumnsDataSort(self,tableName,colNames,orderChoice):
        return self.dbWrapper.getTableColumnsDataSort(tableName,colNames,orderChoice)
Ich persönlich würde diese Form vorziehen, zumal es auch eine Art "Black Box" darstellt. ich warte aber mal, was du dazu meinst ... :wink: Ein nächster schöner Schritt wäre es, im DB-Client per Dialog die entsprechende Datenbank und die Tabellen auszuwählen...

Gruss Tabellar

PS: Vielleicht können wir dann bald den DB-Client für den "SQLator" verwenden ... :wink:
Gast

hallo tabellar,


Hm, ich bin nicht so der Freund von einzelnen SQL Commands in Dictionarys etc. Ich finde, dass die Übersichtlichkeit im Programm verloren geht.
man könnte das dictionary in eine datei auslagern und über pickle laden, irgendwie gefällt es mir besser wenn es für alle funktionen, die schlussendlich dasselbe tun, also z.b. lesen, es eine methode gibt. ich muss dir aber ehrlich sagen, dass das eine bauchsache ist. ich kann das nicht wirklich argumentieren.

wäre interessant zu wissen, was blackjack, leonidas u.a. python-gurus dazu meinen.
Ich persönlich würde diese Form vorziehen, ich warte aber mal, was du dazu meinst ... Wink Ein nächster schöner Schritt wäre es, im DB-Client per Dialog die entsprechende Datenbank und die Tabellen auszuwählen...
ich möchte zuerst wieder die ursprgl. funktionalität herstellen.
davor sollten wir uns einigen wie wir obigen punkt lösen, dann häng ich mich wieder in die tasten, zeitlich wird es bei mir nämlich bald wieder besser :D

liebe grüße

rolgal
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Hi rolgal,

ich hätte zu dem Thema mal folgenden guten Link. und hier auch noch.

Thema hier ist die parametrisierte Form von cursor.execute und die Vermeidung von "SQL-Injection-Angriffen" (quoten der Input Werte durch den Escape-Mechanismus der DB-API). Solange wir diese Dinge bei unseren SQL Commands der Form

Code: Alles auswählen

"select %s from %s order by %s"
beachten, sind wir in der schlussendlichen Implementierung frei.

rolgal hat geschrieben:...man könnte das dictionary in eine datei auslagern und über pickle laden, irgendwie gefällt es mir besser wenn es für alle funktionen, die schlussendlich dasselbe tun, also z.b. lesen, es eine methode gibt. ich muss dir aber ehrlich sagen, dass das eine bauchsache ist. ich kann das nicht wirklich argumentieren.

wäre interessant zu wissen, was blackjack, leonidas u.a. python-gurus dazu meinen.
Ja, gute Idee, vielleicht meldet sich ja der ein oder andere dazu ... :wink: ?
rolgal hat geschrieben:ich möchte zuerst wieder die ursprgl. funktionalität herstellen. davor sollten wir uns einigen wie wir obigen punkt lösen, dann häng ich mich wieder in die tasten, zeitlich wird es bei mir nämlich bald wieder besser :D
Gut. Eine gemeinsame Lösung, die auch noch OPTIMAL im Sinne der DB Abfrage ist, wäre natürlich der Hit.... :P Das sollten wir doch schaffen, oder :wink: ???

Gruassle Tabellar
Gast

hi tabellar,
ich hätte zu dem Thema mal folgenden guten Link. und hier auch noch.

Thema hier ist die parametrisierte Form von cursor.execute und die Vermeidung von "SQL-Injection-Angriffen" (quoten der Input Werte durch den Escape-Mechanismus der DB-API). Solange wir diese Dinge bei unseren SQL Commands der Form
Python-Code:
"select %s from %s order by %s"
beachten, sind wir in der schlussendlichen Implementierung frei.

das heisst nachstehende funktion passt so net, weil es nicht im execute geschieht? ganz blick ich bei der problematik net durch, habe aber in der eile die links nur überflogen.
kannst mir mal schnell in 2, 3 sätzen das verdeutlichen?

Code: Alles auswählen


    def getTableColumnsData(self,a_type,*values):

        action=self.actions[a_type]
        for element in values:
            action=action%element
           
        cursor=self.conn.cursor()
        cursor.execute(action)
        result=cursor.fetchall()
        cursor.close()
        return result 
Gut. Eine gemeinsame Lösung, die auch noch OPTIMAL im Sinne der DB Abfrage ist, wäre natürlich der Hit.... Razz Das sollten wir doch schaffen, oder Wink ???
ein freund sagte in einer meiner krisen mal zu mir: aufgeben tut man briefe...

seit dem halte ich mich dran :D

abgesehen davon gibts hier keine krise, sondern ein großes teil, das halt nur in kleinen schritten zusammengesetzt werden kann


grüße

rolgal
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

tabellar hat geschrieben:Thema hier ist die parametrisierte Form von cursor.execute und die Vermeidung von "SQL-Injection-Angriffen" (quoten der Input Werte durch den Escape-Mechanismus der DB-API). Solange wir diese Dinge bei unseren SQL Commands der Form

Code: Alles auswählen

"select %s from %s order by %s"
beachten, sind wir in der schlussendlichen Implementierung frei.
Solange die DB Module pyformat nutzen, wenn es aber ein anderer paramstyle ist könnte das wiederrum Probleme bereiten.
tabellar hat geschrieben:
rolgal hat geschrieben:wäre interessant zu wissen, was blackjack, leonidas u.a. python-gurus dazu meinen.
Ja, gute Idee, vielleicht meldet sich ja der ein oder andere dazu ... :wink: ?
Ist schon nett als Guru bezeichnet zu werden, jedoch bin ich mit Datenbanken nicht so vertraut, also solltet ihr da besser auf die Meinung von BlackJack warten, bevor ich hier irgendwelchen Schrott erzähle.
Ich persönlich würde aber zur "Black Box"-Variante von Tabellar tendieren, schaut mir irgendwie "sauberer" aus (obwohl die Funktionsnamen etwas kürzer sein dürften *g*).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

rolgal hat geschrieben:... das heisst nachstehende funktion passt so net, weil es nicht im execute geschieht? ganz blick ich bei der problematik net durch, habe aber in der eile die links nur überflogen.
kannst mir mal schnell in 2, 3 sätzen das verdeutlichen?
Ich versuchs mit dem Zitat von Gerhard Häring (sollte allgemein bekannt sein):
Gerhard Häring hat geschrieben: /me setzt Hut als PySQLite-Entwickler auf ;-)

Die Python DB-API 2.0 [*] sieht vor, dass man mit der execute-Methode
eines Cursors-Objekts den SQL-String und die einzufügenden Werte trennt.
Dadurch können die Werte von dem verwendeten Datenbankmodul automatisch
richtig gequotet werden.

>>> import sqlite
>>> sqlite.paramstyle
'pyformat'

PyFormat heisst, dass als Platzhalter %s verwendet werden soll [**].

.execute(operation[,parameters])

=> cursor.execute hat zwei Parameter, der erste ist der SQL-String, der
zweite, optionale, sind die Parameter.

Beispiel:

cu.execute("insert into mytable(a, b) values (%s, %s)", (a, b))

Ganz egal, von welchem Typ die Spalten bzw. Variablen a und b sind, sie
werden vom DB-API Modul richtig gequotet.
Ich hab mich nochmal intensiv mit der Thematik und deinem Funktionsvorschlag beschäftigt. Prinzipiell funktioniert deine Lösung recht gut mit dem SQL-Dict und ist natürlich auch schön kompakt von der Programmierung her. Aber eben nur bei klassischen DB Abfragen (SELECT ... FROM ... ORDER BY ...), da du den SQL-Query (action) vor der cursor.execute Methode schon durch entsprechende Parameterzuweisungen auflöst.

Bei den "INSERT INTO..." Befehlen funktioniert das ganze nicht mehr, wenn man sich an die DB API 2.0 Anweisung halten möchte, da eben in der execute Methode der SQL String und die VALUES getrennt werden sollen. Sprich die Auflösung einer INSERT Action im SQL Dict wie z.B. "INSERT INTO %s %s VALUES %s ;" und eine Trennung bei der cursor.execute(sql,[]) Methode in SQL und Parameter funktionieren nicht mehr.

Gruassle Tabellar
Gast

hallo tabellar,

ganz check ich das noch net, ist aber wohl egal, wir machen einfach mal wie du vorgeschlagen hast.

aber wann weisen wir die tabelle zu ?

cu.execute("insert into mytable(a, b) values (%s, %s)", (a, b))
mfg

rolgal
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Hi rolgal,

bin gerade von .de nach .at gedüst... Ja, die Sache mit der DB API 2.0 execute Geschichte braucht ein wenig Gewöhnungszeit...
rolgal hat geschrieben:hallo tabellar,

ganz check ich das noch net, ist aber wohl egal, wir machen einfach mal wie du vorgeschlagen hast.
aber wann weisen wir die tabelle zu ?
Der SQL Query String wird bis auf die "Values" für die execute Methode vorbereitet, so dass dann SQL und Values übrig bleibt. Hier mal mein Beispiel vom PgSqlDAO Modul:

Code: Alles auswählen

    def insertTableColumnsData(self,tableName,colList,valueList):
        cur=self.con.cursor()        
        cols = ", ".join(colList)
        value_placeholders = ", ".join(["%s"] * len(colList))
        sql = """insert into %(tableName)s (%(cols)s)
                 values (%(value_placeholders)s)""" % locals()
        cur.execute(sql, valueList)
        self.con.commit()
Das ganze sieht wild aus, ergibt sich aber durch die dynamische Spalten und Value Listen. Das ganze ist an Gerhard Härings Methode angelehnt. Das ganze müsste eigentlich 1:1 in MySQL funktionieren.

Gruss Tabellar
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Nach entsprechender Recherche vielleicht doch noch mal ne kurze Erklärung. Die DB API 2.0 Cursor Klasse mit ihrer execute Methode kann optional auch parametrisiert verwendet werden. Diese sog. paramstyles werden von den einzelnen DB API 2.0 Modulen zum Teil unterschiedlich unterstützt. Das SQLite Manual bietet eine gute Erklärung für das Ganze. Ich hab mal ein paar Beispiele zusammengestellt.


PostgreSQL:
PgSQL-Modul
paramstyle='pyformat'

SQLite:
pysqlite2.dbapi2-Modul
paramstyle='qmark'
paramstyle='named'

Code: Alles auswählen

#paramstyle='pyformat'
tableName="myTable"
cols="(feld1,feld2)"
values=('aaa',111)
valueparas="(%s,%s)"
sql="INSERT INTO %(tableName)s %(cols)s VALUES %(valueparas)s" % locals()
cur.execute(sql,values)

Code: Alles auswählen

#paramstyle='pyformat'
cur.execute("INSERT INTO myTable (feld1,feld2) VALUES (%s,%s)",('aaa',111))

Code: Alles auswählen

#paramstyle='qmark'
who = "Yeltsin"
age = 72
cur.execute("select name_last, age from people where name_last=? and age=?", (who, age))

Code: Alles auswählen

#paramstyle='named'
who = "Yeltsin"
age = 72
cur.execute("select name_last, age from people where name_last=:who and age=:age",locals())

Code: Alles auswählen

#paramstyle='named'
who = "Yeltsin"
age = 72
cur.execute("select name_last, age from people where name_last=:who and age=:age",{"who": who, "age": age})
Tabellar :wink:
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

tabellar hat geschrieben:

Code: Alles auswählen

#paramstyle='pyformat'
tableName="myTable"
cols="(feld1,feld2)"
values=('aaa',111)
valueparas="(%s,%s)"
sql="INSERT INTO %(tableName)s %(cols)s VALUES %(valueparas)s" % locals()
cur.execute(sql,values)
Warum arbeitest du eigentlich mich locals() ??? Es geht doch auch so:

Code: Alles auswählen

#paramstyle='pyformat'
sql="INSERT INTO %(tableName)s %(cols)s VALUES %(valueparas)s" % {
        tableName:  "myTable",
        cols:       "(feld1,feld2)",
        valueparas: "(%s,%s)"
    }
    
values=('aaa',111)
cur.execute(sql,values)
Meine Allgemeine SQL Klasse hast du aber auch schon gesehen: http://www.python-forum.de/viewtopic.php?p=20313#20313
Ist zwar speziell für mySQLdb, hilft dir vielleicht aber trotzdem...

Die "valueparas" erzeuge ich z.B. so: ",".join( ["%s"]*len(values) ) für "%s" könnte man auch gleich einen String nehmen, den man durch den paramstyle festlegt... Wobei das so einfach nur für 'qmark' und 'format' klappt :(

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten