In MySQL mit einem Fremdschlüssel Daten eintragen (for loop)

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Brawn
User
Beiträge: 11
Registriert: Dienstag 21. Januar 2014, 22:38

Guten Tag,

Ich habe ein Programm für mein Raspi geschrieben (um Python zu erlernen) :D
Mein Programm schreibt Automatisch von einen der 5 Sensoren die Werte inkl. Zeit und Datum in eine Textdatei.
Mit einem seperaten Programm (da ich später auf Client / Server gehen will) kann ich die Textdatei öffnen und in einer for schleife dies in eine MySQL Datenbank übertragen.
Leider habe ich jetzt die DB ein wenig Optimiert (Menge an Daten) und möchte noch in der for schleife den Fremdschlüssel erfragen.

Hier ist mal das Programm für die Übertragung zum SQL Server:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys, csv
from time import *
from bin import database

db = database()
fdate = strftime("%Y-%m-%d")
ftime = strftime("%H:%M:%S")
timewr = (fdate +"_"+ ftime +"_"+"temp-data.tlist")

def sensidr():
	query = "SELECT idsensor FROM sensor WHERE sensorid = '%s';" % (sensid);
	cmdr = db.query(query)
	for id in cmdr:
		idr = '%s' % id['idsensor']
	return idr

if os.path.isfile('temp-data-transfer.txt'):
	data = csv.reader(open('temp-data-transfer.txt', 'r'), delimiter=';')
	for row in data:
		sdate=row[0]
		stime=row[1]
		sensid=row[2]
		sdata=row[3]
		projectid="1"
		print sdate, stime, sensid, sdata, projectid, sensidr
		query = "INSERT INTO `sensdata` (sdate, stime, sdata, project_idproject, sensor_idsensor) VALUES ('%s', '%s', '%s', '%s', '%s');" % (sdate, stime, sdata, projectid, sensidr);
		db.insert(query)
	os.system("mv temp-data-transfer.txt messdata/"+timewr)
else:
	print "fehler bei der Uebertragung zu SQL"
	exit(0)
Ich dachte mir mit einer Funktion wäre es schöner geregelt, aber leider bekomme ich den wert von der for schleife "sensid" nicht in die Funktion "sensidr".
Bzw. bekomme eine solche Meldung:

Code: Alles auswählen

2013-12-24  00:35:03  28-0000050a6a69  17.937 1 <function sensidr at 0x1244de8>
Wenn ich die "sensid" mit einer Variable Statisch versehe geht es:

Code: Alles auswählen

sensid="28-0000050a6a69"

query = "SELECT idsensor FROM sensor WHERE sensorid = '%s';" % (sensid);
cmdr = db.query(query)
for id in cmdr:
	idr = '%s' % id['idsensor']
print idr
Ausgabe: Oder könnte man dies wie bei einem SQL Server, auch den Fremdschlüssel direkt erfragen?

Code: Alles auswählen

INSERT INTO `sensdata` (sdate, stime, sdata, project_idproject, sensor_idsensor) VALUES ('2013-12-17', '22:05:03', '19.812', '1', (select idsensor from sensor where sensorid ='28-0000050a6a69'));
Wie wäre dieser Befehl unter Python ?

Lieber wäre aber zu Wissen ob es überhaupt geht aus einer for schleife einen Wert in die Funktion einzutragen?

Vielen Dank schonmal für die Hilfe. 8)

PS.: Ich bin für Fairplay und möchte nur sagen das ich dies im Linux-forum.de auch gepostet habe.
Bis jetzt aber leider keine Antwort bekommen habe :?
BlackJack

@Brawn: Du formatierst da die *Funktion* in die SQL-Anweisung statt die Funktion *aufzurufen* und damit den Rückgabewert als Wert in die SQL-Anweisung zu formatieren.

Das ist aber grundsätzlich alles ziemlich unschön. Viele Namen sind unverständlich (`sensidr`, `sensid`, `cmdr`, `timewr`, `bin`, …). Namen sollen dem Leser sagen was der Wert bedeutet und nicht zum Rätselraten zwingen. Damit verbieten sich im Grunde Abkürzungen die nicht allgemein bekannt sind.

Sternchenimporte sind keine gute Idee, dann kann man das Konzept von Modulen auch gleich bleiben lassen.

Einrückung ist üblicherweise vier Leerzeichen pro Ebene.

Auf Modulebene sollten nur Konstante Werte definiert werden und Funktionen sollten nur auf Konstanten von ausserhalb und übergebene Argumente zugreifen. Sonst hat man am Ende einen grossen Klumpen Code bei dem die Abhängigkeiten schlecht zu durchschauen sind.

Die beiden Semikolons an den Zeilenenden sind überflüssig. Das Semikolon trennt Anweisungen innerhalb einer Zeile. Da mehr als eine Anweisung pro Zeile den Quelltext schwerer lesbar macht, wird das in Python nur sehr selten verwendet.

Werte selber per Zeichenkettenoperationen in SQL-Anweisungen zu basteln ist eine Sicherheitslücke. Das überlässt man dem Datenbankmodul.

``'%s' % id['idsensor']`` ist eine komische Art ``str(id['idsensor'])`` zu schreiben. Wobei man das an der Stelle wahrscheinlich sowieso nicht in eine Zeichenkette wandeln sollte.

Der Test ob es sich um eine Datei handelt ist redundant, weil das beim Verarbeiten sowieso passiert und dann entsprechend eine Ausnahme auslöst die man behandeln kann.

Dateien die man öffnet, sollte man auch explizit wieder schliessen. Da bietet sich die ``with``-Anweisung an.

Wenn die Datei 4 Werte pro Zeile hat, kann man die Schleife deutlich kürzer schreiben, in dem man ``for row in`` durch ``for sdate, stime, sensid, sdata in`` ersetzt. Die `projectid` bei jedem Durchlauf auf '1' zu setzen ist redundant, das hätte man auch vor der Schleife *einmal* machen können.

Zum Verschieben einer Datei muss man keine externe Shell starten die dann ``mv`` startet. Da gibt es im `os`-Modul eine Funktion für. Das zusammensetzen von Teilpfaden macht man mit `os.path.join()`.

Den Dateinamen dreimal im Quelltext auszuschreiben ist Fehleranfällig und macht unnötige Arbeit wenn man ihn ändern will.

Das ``exit(0)`` ist überflüssig.

Vielleicht magst Du ja mal ein Grundlagentutorial durcharbeiten um Funktionen zu lernen.
Brawn
User
Beiträge: 11
Registriert: Dienstag 21. Januar 2014, 22:38

@BlackJack: Vielen Dank für die schnelle Antwort.
Ich weiss das ich erst ein Anfänger bin und werde auch die Ratschläge soweit ich verstehen kann auch Umsetzen.
Du formatierst da die *Funktion* in die SQL-Anweisung statt die Funktion *aufzurufen* und damit den Rückgabewert als Wert in die SQL-Anweisung zu formatieren.
Sorry, da stehe ich jetzt auf der Leitung !?
Das ist aber grundsätzlich alles ziemlich unschön. Viele Namen sind unverständlich (`sensidr`, `sensid`, `cmdr`, `timewr`, `bin`, …). Namen sollen dem Leser sagen was der Wert bedeutet und nicht zum Rätselraten zwingen. Damit verbieten sich im Grunde Abkürzungen die nicht allgemein bekannt sind.
Da hast du Recht, und werde es mir auch hinter die Ohren schreiben. 8)
Einrückung ist üblicherweise vier Leerzeichen pro Ebene.
Hab einen TAB pro Ebene verwendet.
Wenn die Datei 4 Werte pro Zeile hat, kann man die Schleife deutlich kürzer schreiben, in dem man ``for row in`` durch ``for sdate, stime, sensid, sdata in`` ersetzt. Die `projectid` bei jedem Durchlauf auf '1' zu setzen ist redundant, das hätte man auch vor der Schleife *einmal* machen können.
Habe ich jetzt mal versucht aber leider sagt mir Python 2.7 das ich zu viele Argumente liefere (Werde da noch mal dies durcharbeiten).
die for schleife funktioniert ja auch, leider kann ich aber nicht einen Wert aus der for schleife in die funktion einbauen.
Um dabei den Fremdschlüssel zu bekommen.
Wegen der "Projectid" dies habe ich jetzt aus der Schleife rausgenommen (da hast du ganz einfach Recht) :)
Zum Verschieben einer Datei muss man keine externe Shell starten die dann ``mv`` startet. Da gibt es im `os`-Modul eine Funktion für. Das zusammensetzen von Teilpfaden macht man mit `os.path.join()`.
Werd es ansehen und tauschen.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Brawn hat geschrieben:
Du formatierst da die *Funktion* in die SQL-Anweisung statt die Funktion *aufzurufen* und damit den Rückgabewert als Wert in die SQL-Anweisung zu formatieren.
Sorry, da stehe ich jetzt auf der Leitung !?
Du versuchst sensidr in die Datenbank zu schreiben. Das ist aber eine Funktion. Wenn du die Funktion aufrufen und den Rückgabewert benutzen willst, dann muss es sensidr() heißen.
Brawn hat geschrieben:Hab einen TAB pro Ebene verwendet.
Verwende 4 Leerzeichen, das ist Standard. Wirf bei Gelegenheit mal einen Blick auf den Style Guide for Python Code.
Brawn
User
Beiträge: 11
Registriert: Dienstag 21. Januar 2014, 22:38

Danke für eure Hilfe. :mrgreen:
Jetzt habe ich mal das Skript ein wenig angepasst, und gehe auch noch die Python Doku nochmal durch:
Hab auch schon versucht die sensoruuid mit global zu legen.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import csv
from time import *
from bin import database

#sensoruuid = []
#global sensoruuid
db = database()
datum = strftime("%Y-%m-%d")
uhrzeit = strftime("%H:%M:%S")
dateiname = (datum +"_"+ uhrzeit +"_"+"temp-data.tlist")
projectid = "1"


def sensorfk():
    select_query = "SELECT idsensor FROM sensor WHERE sensorid = '%s';" % (sensoruuid);
    cmdr = db.query(select_query)
    for id in cmdr:
        sensorfk = "%s" % id['idsensor']
    return


#if __name__ == "__main__":

if os.path.isfile('temp-data-transfer.txt'):
    print "dateiumschreibung schon geschehen"
else:
    os.system("mv temp-data.txt temp-data-transfer.txt")

data = csv.reader(open('temp-data-transfer.txt', 'r'), delimiter=';')
for row in data:
    messdatum=row[0]
    messzeit=row[1]
    sensoruuid=row[2]
    messung=row[3]
    print sensoruuid, sensorfk()
Dabei bekomme ich leider folgende Ausgabe:

Code: Alles auswählen

 python exporttosql.py
dateiumschreibung schon geschehen
 28-0000050ae144 None
 28-0000050a6a69 None
 28-0000050ae144 None
 28-0000050a6a69 None
 28-0000050ae144 None
 28-0000050a6a69 None
 28-0000050ae144 None
 28-0000050a6a69 None
 28-0000050ae144 None
 28-0000050a6a69 None
 28-0000050ae144 None
 28-0000050a6a69 None
Besser gesagt wie kann man sonst unter Python den Fremdschlüssel für einen Insert herausbekommen ?

Hier auch noch zur Sicherheit die Klasse database:

Code: Alles auswählen

class database:
        #Home
        host = '192.168.xxx.xxx'
        db = 'test'
        user = 'test'
        passwd = 'test'

        def __init__(self):
                self.connection = MySQLdb.connect(self.host, self.user, self.passwd, self.db)
                self.cursor = self.connection.cursor()

        def insert(self, query):
                try:
                        self.cursor.execute(query)
                        self.connection.commit()
                except:
                        self.connection.rollback()

        def query(self, query):
                cursor=self.connection.cursor( MySQLdb.cursors.DictCursor )
                cursor.execute(query)

                return cursor.fetchall()

        def __del__(self):
                self.connection.close()

BlackJack

@Brawn: Funktionen die nichts zurück geben, geben `None` zurück. Und die `sensor_id` solltest Du als Argument übergeben und nicht einfach so annehmen das es die ausserhalb der Funktion einfach gibt. Denn auf Modulebene sollte es die eigentlich auch nicht geben. Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden. Das Schlüsselwort ``global`` vergisst Du am besten sofort wieder.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

`__del__` solltest du besser auch vergessen und die Datenbankverbindung nur dann oeffnen, wenn du sie brauchst, und dann wieder schliessen. Die `with`-Anweisung bietet sich hier an, evtl in Verbindung mit `contextlib.closing`.
Brawn
User
Beiträge: 11
Registriert: Dienstag 21. Januar 2014, 22:38

Vielen dank für die Hilfe.
Aber leider weiss ich noch immer nicht wie ich die ID rausbekomme?.

Finde auch nichts richtiges dazu im Internet, als ob man dies nie brauchen würde. :K

Werde jetzt mal die SQL-Verbindung durcharbeiten.
BlackJack

@Brawn: Natürlich findet sich nichts im Internet zu *Deinem* Datenbankinhalt, denn *das* ”Problem” hast ja tatsächlich nur Du, weil kein anderer Deinen konkreten Datenbankentwurf hat. Du hast bis jetzt aber deutliche Verständnisprobleme von Python-Grundlagen gezeigt die mit Datenbanken rein gar nichts zu tun haben. Und dafür sollte es mehr als genug Lesestoff geben.
Brawn
User
Beiträge: 11
Registriert: Dienstag 21. Januar 2014, 22:38

Hallo, so habe es endlich herausgefunden. :mrgreen:

Und zwar wenn er die "sensoruuid" liest ist davor in der TXT Datei noch ein Leerzeichen, und dies kann er natürlich in der DB nicht finden.
Was ja richtig ist.
Oh Mann, da geht Zeit drauf und erst jetzt sehe ich es das es immer Abstände vor der Sensoruuid gibt. :? :oops:

Jedenfalls vielen Dank für eure Hilfe und Unterstützung.

@BlackJack
Du hast ja recht, aber leider sah ich den Wald vor lauter Bäumen nicht (meinen Fehler meinte ich). :mrgreen:
Antworten