CherryPy: Firmenverwaltung

Django, Flask, Bottle, WSGI, CGI…
Antworten
breedi
User
Beiträge: 10
Registriert: Freitag 28. Oktober 2016, 18:50

Guten Abend,

Ich habe folgendes Problem. Ich muss für meine Universität eine Firmenverwaltung schreiben und bin bisher recht weit gekommen. Jedoch bin ich bei meiner Bearbeitungsfunktion hängen geblieben. Ich möchte über mein URL-Parameter jeweils eine Form erzeugen, der Wert ist die jeweilige ID für den Bearbeitungsvorgang, die in der JSON-Datei meinen Eintrag ersetzt und habe die Vorarbeit dafür schon geleistet, sobald die Anforderung gesendet wird, erkennt mein CherryPy-Server dies nicht und leitet nicht gewollt in mein Safe-Formular. Bin auch gerne für ein Skypegesprächverfügbar, sollte die Hilfestellung mehr Zeit benötigen.

Die Parameteraufrufe lasse ich in den list.tpl's generieren und da findet auch der URL-Aufrufe per Link statt.

Bitte keine Verweise zur Benutzung von Django. Unser Professor hat nicht gestattet, dieses Framework zu benutzen.

Meine Anwendung sieht wie folgt auf dem Bild aus:
Startseite:
Bild
Erfassen:
Bild

Über Hilfestellung wäre ich sehr dankbar.

Code: Alles auswählen

# coding: utf-8

import cherrypy

from .database import Database_cl
from .view import View_cl

#----------------------------------------------------------
class Application_cl(object):
#----------------------------------------------------------

    #-------------------------------------------------------
    def __init__(self):
    #-------------------------------------------------------
        # spezielle Initialisierung können hier eingetragen werden
        self.db_o = Database_cl()
        self.view_o = View_cl()
		
    @cherrypy.expose
	#-------------------------------------------------------
    def index(self):
	#-------------------------------------------------------
	    return self.createList_p()
		
    @cherrypy.expose
	#-------------------------------------------------------
    def add(self):
	#-------------------------------------------------------
	    return self.createForm_p()
		
    cherrypy.expose
	#-------------------------------------------------------
    def edit(self,id):
	#-------------------------------------------------------
	    return self.createForm_p(id)
		
    @cherrypy.expose
	#-------------------------------------------------------
    def save(self, **data_opl):
	#-------------------------------------------------------
	    # Sichern der Daten: aufgrund der Formularbearbeitung muss
		# eine vollständige HTML-Seite zurückgeliefert werden!
		
		# data_opl: Dictionary mit den gelieferten key-value-Paaren
		
		# hier müsste man prüfen, ob die Daten korrekt vorliegen!
		
	    id_s = data_opl["id_s"]
	    data_a =[ data_opl["firmenname"]
	    ,   data_opl["branche"]
	    ,   data_opl["taetigkeit"]
	    ,   data_opl["sitz"]
	    ,   data_opl["anzahl"]

		]
	    if id_s != "None":
		    # Update-Operation
		    self.db_o.update_px(id_s, data_a)
	    else:
			# Create-Operation
		    id_s = self.db_o.create_px(data_a)
			
	    return self.createForm_p(id_s)
		
    @cherrypy.expose
	#-------------------------------------------------------
    def delete(self, id):
	#-------------------------------------------------------
	    # Eintrag löschen, dann Liste neu anzeigen
		
	    self.db_o.delete_px(id)
	    return self.createList_p()
		
    @cherrypy.expose
	#-------------------------------------------------------
    def default(self, *arguments, **kwargs):
	#-------------------------------------------------------
	    msg_s = "unbekannte Anforderung: " + \
		str(arguments) + \
		' ' + \
		str(kwargs)
	    raise cherrypy.HTTPError(404, msg_s)
    default.exposed = True
	
	#-------------------------------------------------------
    def createList_p(self):
	#-------------------------------------------------------
	    data_o = self.db_o.read_px()
		# mit diesen Daten Markup erzeugen
	    return self.view_o.createList_px(data_o)
		
	#-------------------------------------------------------
    def createForm_p(self, id_spl = None):
    #-------------------------------------------------------
	    if id_spl != None:
		    data_o = self.db_o.read_px(id_spl)
	    else:
		    data_o = self.db_o.getDefault_px()
		# mit diesen Daten Markup erzeugen
	    return self.view_o.createForm_px(id_spl, data_o)
		
# EOF

Code: Alles auswählen

# coding: utf-8

import os
import os.path
import codecs

import json

#----------------------------------------------------------
class Database_cl(object):
#----------------------------------------------------------
    # da es hier nur darum geht, die Daten dauerhaft zu speichern,
    # wird ein sehr einfacher Ansatz verwendet:
	
	
	# - es können Daten zu genau 15 Teams gespeichert werden
	# - je Team werden 2 Teilnehmer mit Namen, Vornamen und Matrikelnummer
	#   berücksichtigt
	# - die Daten werden als eine JSON-Datei abgelegt
	
	#-------------------------------------------------------
	def __init__(self):
	#-------------------------------------------------------
	    self.data_o = None 
	    self.readData_p()
		
	#-------------------------------------------------------
	def create_px(self, data_opl):
    #-------------------------------------------------------
	    # Überprüfung der Daten müsste ergänzt werden!
		
		# 'freien' Platz suchen,
		# falls vorhanden: belegen und Nummer des Platzes als Id zurückgeben
		
		id_s = None
		for loop_i in range(0,15):
		    if self.data_o[str(loop_i)][0] == '':
			    id_s = str(loop_i)
			    self.data_o[id_s] = data_opl
			    self.saveData_p()
			    break
				
		return id_s
		
	#-------------------------------------------------------
	def read_px(self, id_spl  = None):
	#-------------------------------------------------------
	    # hier zur Vereinfachung:
		# Aufruf ohne id: alle Einträge liefern
		data_o = None
		if id_spl == None:
		    data_o = self.data_o
		else:
		    if id_spl in self.data_o:
			    data_o = self.data_o[id_spl]
			
		return data_o
		
	#-------------------------------------------------------
	def update_px(self, id_spl, data_opl):
	#-------------------------------------------------------
		# Überprüfung der Daten müsste ergänzt werden!
		status_b = False
		if id_spl in self.data_o:
			self.data_o[id_spl] = data_opl
			self.saveData_p()
			status_b = True
			
		return status_b
		
	#-------------------------------------------------------
	def delete_px(self, id_spl):
    #-------------------------------------------------------
		status_b = False
		if id_spl in self.data_o:
			
			# hier müssen Sie den Code ergänzen
			# Löschen als Zurücksetzen auf die Default-Werte implementieren
		   self.data_o[id_spl] = self.getDefault_px()
		   self.saveData_p()
		   status_b = True		        
		#Ihr Ergänzung
		return status_b
		
	#-------------------------------------------------------
	def getDefault_px(self):
	#-------------------------------------------------------
	    return['','','','','','']
		
	#-------------------------------------------------------
	def readData_p(self):
	#-------------------------------------------------------
	    try:
		    fp_o = codecs.open(os.path.join('data','webteams.json'),'r','utf-8')
	    except:
		    # Datei neu anlegen
		    self.data_o = {}
		    for loop_i in range(0,15):
			    self.data_o[str(loop_i)]=['','','','','','']
			    self.saveData_p()
	    else:
		    with fp_o:
			    self.data_o = json.load(fp_o)
				
	    return
		
	#-------------------------------------------------------
	def saveData_p(self):
	#-------------------------------------------------------
	    with codecs.open(os.path.join('data','webteams.json'),'w','utf-8')as fp_o:
		    json.dump(self.data_o, fp_o)
			
# EOF

Code: Alles auswählen

# coding: utf-8

# sehr einfache Erzeugung des Markups für vollständige Seiten
# jeweils 3 Abschnitte:
# - begin
# - content
# - end

# bei der Liste wird der content-Abschnitt wiederholt
# beim Formular nicht
import codecs
import os.path
import string
#----------------------------------------------------------
class View_cl(object):
#----------------------------------------------------------

    #-------------------------------------------------------
    def __init__(self):
	#-------------------------------------------------------
	    pass
		
	#-------------------------------------------------------
    def createList_px(self, data_opl):
	#-------------------------------------------------------
	    # hier müsste noch eine Fehlerbehandlung ergänzt werden !
	    markup_s =''
	    markup_s += self.readFile_p('list0.tpl')
		
	    markupV_s = self.readFile_p('list1.tpl')
	    lineT_o = string.Template(markupV_s)
		# mehrfach nutzen, um die einzelnen Zeilen der Tabelle zu erzeugen
	    for loop_i in range(0,15):
	        data_a = data_opl[str(loop_i)]
	        markup_s += lineT_o.safe_substitute (firmenname = data_a[0]
		    ,   branche = data_a[1]
		    ,   taetigkeit = data_a[2]
		    ,   sitz = data_a[3]
		    ,   anzahl = data_a[4]

		    ,   id_s = str(loop_i)
		    )
		
	    markup_s += self.readFile_p('list2.tpl')
		
	    return markup_s
	#-------------------------------------------------------
    def createForm_px(self, id_spl, data_opl):
	#-------------------------------------------------------
	
	    # hier müsste noch eine Fehlerbehandlung ergänzt werden !
	    markup_s =''
	    markup_s += self.readFile_p('form0.tpl')

	    markupV_s = self.readFile_p('form1.tpl')
	    lineT_o = string.Template(markupV_s)
	    markup_s += lineT_o.safe_substitute (firmenname = data_opl[0]
	    ,   branche = data_opl[1]
	    ,   taetigkeit = data_opl[2]
	    ,   sitz = data_opl[3]
	    ,   anzahl = data_opl[4]

	    ,   id_s = id_spl
	    )

	    markup_s += self.readFile_p('form2.tpl')
		 
	    return markup_s


	#-------------------------------------------------------
    def readFile_p(self, fileName_spl):
	#-------------------------------------------------------
	    content_s =''
	    with codecs.open(os.path.join('template', fileName_spl),'r','utf-8')as fp_o:
		    content_s = fp_o.read()
			
	    return content_s
# EOF
Form0.tpl:
[codebox=html5 file=Unbenannt.html]<!DOCTYPE html>
<html>
<head>
<title>Web-Teams</title>
<meta charset="UTF-8"/>
<style>
@import "/webteams.css";
</style>
<script type="text/javascript" src="/webteams.js"></script>
</head>
<body>
<h2>Anlegen eines neuen Datensatzes</h2>
<hr>
<form id="idWTForm" action="/save" method="POST">[/code]

Form1.tpl
[codebox=html5 file=Unbenannt.html]<input type= "hidden" value="$id_s" id="id_s" name="id_s"/>
<div>
<label for= "firmenname">Firmenname</label>
<input type="text" value="$firmenname" id="firmenname" name="firmenname" required/>
</div>
<div>
<label for="branche">Branche</label>
<input type="text" value="$branche" id="branche" name="branche" required/>
</div>
<div>
<label for="taetigkeit">Tätigkeit</label>
<input type="text" value="$taetigkeit" id="taetigkeit" name="taetigkeit" required/>
</div>

<div>
<label for= "sitz">Sitz</label>
<input type="text" value="$sitz" id="sitz" name="sitz" required/>
</div>
<div>
<label for="anzahl">Anzahl</label>
<input type="text" value="$anzahl" id="anzahl" name="anzahl" required/>
</div>
[/code]

Form2.tpl
[codebox=html5 file=Unbenannt.html] <div>
<input type="submit" value="Speichern" />
<a href="/" >Abbrechen<a/>
</div>
</form>
</body>
</html>[/code]

[codebox=html5 file=list0.tpl]
<!DOCTYPE html>
<html>
<head>
<title>Web-Teams</title>
<meta charset="UTF-8"/>

<style>
@import "/webteams.css";
@import "/style.css";
</style>
<script type="text/javascript" src="/webteams.js"></script>
</head>
<body>
<table>
<tr>
<th>Firmenname</th><th> Branche </th><th>Tätigkeitsschwerpunkt</th>
<th> Sitz </th><th> Anzahl Mitarbeiter</th>
<th>Aktion</th>
</tr>
[/code]

[codebox=html5 file=list1.tpl]<tr>
<td>$firmenname</td><td>$branche</td><td>$taetigkeit</td>
<td>$sitz</td><td>$anzahl</td>
<td><a href="/edit/$id_s">Bearbeiten</a>&nbsp;<a class="clDelete" href="/delete/$id_s">Löschen</a></td>
</tr>

[/code]

[codebox=html5 file=list2.tpl]
</table>
<div>
<form method = "GET" action ="/add">
<button type="submit">Erfassen</button>
</form>
</div>
</body>
</html>[/code]


Meine JASON-Datei ist recht simpel aufgebaut:

[codebox=j file=webteams.json]{"4": ["", "", "", "", ""], "12": ["", "", "", "", ""], "13": ["", "", "", "", ""], "3": ["", "", "", "", "", ""], "10": ["", "", "", "", ""], "0": ["dwd", "dwdwd", "wdwd", "dwdwd", "dwdw"], "2": ["test", "test", "test", "testtest", "test"], "11": ["", "", "", "", ""], "1": ["sadads", "asdasd", "asdsadsd", "asdsad", "asdsda"], "7": ["", "", "", "", ""], "6": ["", "", "", "", ""], "9": ["", "", "", "", "", ""], "8": ["", "", "", "", ""], "14": ["", "", "", "", "", ""], "5": ["", "", "", "", "", ""]}[/code]
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ich verstehe gerade aus deiner noch nicht, wo dein Problem liegt. Wird die Edit-Form nicht gefüllt oder bekommst du die korrekte Edit-Form, aber das Speichern klappt nicht?

Ansonsten ist dein Quelltext schwer zu lesen, weil du äußerst kryptische Variabelnamen und teilweise auch Funktionsnamen nutzt. Da ist überhaupt nicht klar, wozu was gut ist.

Ist das Nutzen von CherryPy Vorgabe vom Prof? Wobei dein Problem an sich ja nicht CherryPy ist, sondern eher, wie du wo was prüfst.

Gruß, noisefloor
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@breedi: ich habe versucht Deinen Code zu lesen, was war nicht leicht. Was sollen denn die Kommentarzeilen mit -----? Durch die Einrückung wird doch schon klar sichtbar, was zusammengehören soll. Dann diese Typ-Endungen. Klassen erkennt man an der Großschreibung, da braucht es kein _cl. Funktionen erkennt man, dass der Name eine Tätigkeit ausdrückt, Variablen auch durch aussagekräftige Namen, statt id_s oder data_o. Pro Datei eine Klasse erinnert auch mehr an Java als an Python. Statt der Kommentare am Anfang von Klassen und Funktionen kennt Python Doc-Strings. »for loop_i in range(0,15):« ist ein Anti-Pattern, man iteriert direkt über die Key-Value-Paare. Das JSON-Format ist etwas seltsam. Eine Liste stellst Du durch ein Wörterbuch mit Schlüsseln "0" bis "14" an und dort wo ein Wörterbuch sinnvoll wäre (Name, Vorname, MatNr) nimmst Du eine Liste. Hat CherryPy kein eigenes Template-System, dass man ein Template auf drei Dateien aufteilen muß. Das kann ich eigentlich nicht glauben. Statt Strings mit + zusammenzustückeln will man ja gerade ein Template-System benutzen.
breedi
User
Beiträge: 10
Registriert: Freitag 28. Oktober 2016, 18:50

Den Quellcode hatte uns unser Professor als Vorlage gegeben und wir sollten damit arbeiten. Mein konkretes Problem ist die Bearbeiten-Funktion ( edit ), die nicht funktioniert

Es ist genauso vorgegangen worden, wie bei der Erfassungs/Löschenfunktion; die beide auch funktionieren, jedoch crashed es serverseitig und ich finde den Fehler einfach nicht. Er erkennt nicht, dass er die Form erstellen soll, ansonsten habe ich keinen Ansatz
Bild

@Sirius3:
Das ist seine Art zu programmieren und er will uns diese beibringen. Das die kryptisch ist und schwer zu lesen ist unter anderem auch ein Problem mit dem ich zu kämpfen habe, aber ich habe seine Programmierart weiter eingehalten. Ich habe mir auch Beispiele von anderen Programmiersprachen angeschaut, wie sie Formulardaten serverseitig erhalten, was sich als wesentlich lesbarer erfasst z.B PHP

Onlinedokumentation von CherryPy bietet nur triviale Beispiele die absolut keine Hilfe sind. (Wobei ich noch erwähnen muss, dass CherryPy sich selsbt als minimalistisches Framework sieht und ich schätze mal deßhalb nciht so flächendeckend ist wie z.B Django was uns Arbeit ersparen würde) Die JSON-Datei ist ja unsere Dateiablage sowie das Dictonary das 15 Einträge fassen kann. Zum Templatesystem denke ich mal, dass wir einfach verstehen soll wie sowas abläuft und es selbst so programmieren, im Hintergrund läuft in solchen Funktionen schätze ich mal das Gleiche ab.

Das Ziel was er damit verfolgt, weiß ich selbst nicht. Das wir wissen sollen, wie es sich im Hintergrund abspielt. Ich habe vorher mit C++ entwickelt und bin kompletter Einsteiger in Python. Das war z.B das erste Praktikum und er wollte uns ich zitiere: "in kalte Wasser werfen"
BlackJack

@breedi: Wenn keine Verweise zu Django kommen sollen, dann schau Dir mal Bottle oder Flask an. ;-)

Was an Deinem Quelltext als erstes auffällt sind furchtbaren, kryptischen Typsuffixe bei den Namen. Wenn das keine Vorgabe vom Professor ist, dann lass das bleiben. Falls es eine Vorgabe ist, wechsel den Professor. Der ist dann gefährlich. :twisted:

Ansonsten haben Grossbuchstaben in allem ausser Konstantennamen und Klassennamen nichts zu suchen. Siehe Style Guide for Python Code.

Die Aufteilung der Klassen in Application und View ist eigenartig, zumal beide Klassen eigentlich gar nicht wirklich Klassen sind. Aber CherryPy zwingt Nutzer leider dazu solche unechten Klassen zu schreiben. Ein Grund warum ich eher zu einem anderen Rahmenwerk raten würde. Man lernt bei CherryPy wie man es eher *nicht* machen sollte. Das ist wohl auch der Grund warum es nicht so weit verbreitet ist, denn andere Microrahmenwerke sind durchaus verbreitet. Bottle oder Flask beispielsweise.

Die selbstgebastelten und auf mehrere Dateien zerstückelten HTML-Vorlagen machen das undurchsichtig. Und ausserdem kann man als böser Mensch Eingaben machen die dann später 1:1 in die HTML-Ausgabe hineinformatiert werden und damit im besten Fall nur die Webseitenanzeige kaputt machen, im schlechten Fall den Angreifer beliebigen JavaScript-Code in Deine Seite einbauen lassen. Im „Programmer's Guide“ in der CherryPy-Dokumentation steht gleich am Anfang das Kapitel „Choosing a templating language“. Such Dir eine aus und verwende die. (Ich persönlich mag Jinja2 ganz gerne.)

Eine Template-Engine macht mehr als was Du da in den Funktionen machst. Neben dem escapen von Zeichen die in HTML eine besondere Bedeutung haben, und damit auch der Absicherung das jemand von aussen HTML und JavaScript in Deine Webseite einschmuggeln kann, bieten die eine Möglichkeit Anzeige und Programmlogik sauber zu trennen.

Die Datenhaltung in dem JSON ist etwas umständlich mit den Zeichenketten mit ganzen fortlaufenden Zahlen als Schlüsseln. Da hätte man auch einfach ein Array mit Array pro Zeile mache können und den Index als in das Array als ”Datenbankschlüssel” verwenden können.

Die Zeile ``default.exposed = True`` macht keinen Sinn — weg damit.

Ebenso hat ``import os.path`` keinerlei Effekt weil das passende `path`-Modul schon beim importieren von `os` automatisch importiert wird.

Statt `codecs.open()` würde man heute `io.open()` verwenden.

Wenn man auf einen Schlüssel in einem Wörterbuch prüft bevor man darauf zugreift sollte man sich mal die `get()`-Methode anschauen.

Statt in `delete_px` fast den ganzen Code aus `update_px()` zu wiederholen, hätte man das Löschen durch einen simplen Aufruf von `update_px()` mit den passenden Argumenten erledigen können.

In einer Methode die `readData_p()` heisst, sollten Daten wirklich nur gelesen und nicht geschrieben werden. Zudem passiert das auch noch sehr ineffizient, es werden 15 Datensätze angelegt und bei jedem Datensatz werden alle Datensätze immer wieder neu geschrieben. Statt die erst alle anzulegen und dann am Ende alle nur *einmal* zu schreiben.

Eine `__init__()` die nur ``pass`` enthält macht keinen Sinn. Wech damit.

Laut der Meldung im Browser wurde eine nicht vorhandene URL abgefragt. Was soweit ich das sehe auch stimmt, denn Du hast da nirgends Code der '…/edit/0' verarbeitet. Es gibt nur '…/edit' was einen Parameter `id_s` erwartet. Also sowas wie '…/edit?id_s=13' als URL würde an diese Methode weitergeleitet.
BlackJack

@breedi: Okay, Sorry das war Unsinn. Die positionalen Argumente sind nicht für URL-Parameter. Dann weiss ich auch nicht woran es liegt.
breedi
User
Beiträge: 10
Registriert: Freitag 28. Oktober 2016, 18:50

Hallo BlackJack,

Ersteinmal herzlichen Dank für deine umfassende Antwort. Ich versuche mich am Style Guide for Python zukünftig zu halten. Wie ich bereits merke ist der Aufschrei gegen seine Programmierweise sehr groß und ich tendiere immer weiter die Struktur meiner Webpage in Frage zu stellen und fange ggf. nochmal von vorne an. Wie bereits vorher erwähnt, entstand dieser Quellcode aus Vorlage unseres Professors der eher konservativ eingestellt ist. Leider ist es uns nicht erlaubt das Framework zu wechseln und ich muss CherryPy benutzen, daran komm ich nicht vorbei.

In der Onlinedocumentation (http://docs.cherrypy.org/en/latest/tutorials.html) bin ich noch bei den Tutorials / Basics am rumwühlen und habe den Teil mit den Templates noch nicht gesehen, aber ich bin mir nicht sicher ob wir diese verwenden dürfen. Ich habe auch keineswegs verglichen, das meine Templatefunktionen genauso sind wie die von den Frameworks, ich denke er wollte uns nur zeigen wie das auf elementarster Ebene funktioniert.

Bezüglich deiner Aussage mit der Datenhaltung im JSON-Format.
Die Datenhaltung in dem JSON ist etwas umständlich mit den Zeichenketten mit ganzen fortlaufenden Zahlen als Schlüsseln. Da hätte man auch einfach ein Array mit Array pro Zeile mache können und den Index als in das Array als ”Datenbankschlüssel” verwenden können.

Aus seiner Aufgabenstellung:

Bild

Er hatte noch erwähnt, das wir das Modell nicht als Datenbank realisieren sollen, deswegen bin ich mir nicht sicher, ob das so einfach geht wie du es verschlägst. Das Array was du vorschlägst würde ja Serverseitig agieren, jedoch sollen wir die JSON Datei als Ablage verwenden. Steht auch in der Aufgabenstellung explizit drin. ( Mit der Datenbank ist dies nicht hier im Aufgabentext enthalten, aber ich habe es noch schriftlich als E-Mail Aussage sowie mündlich aus der Vorlesung)
Laut der Meldung im Browser wurde eine nicht vorhandene URL abgefragt. Was soweit ich das sehe auch stimmt, denn Du hast da nirgends Code der '…/edit/0' verarbeitet. Es gibt nur '…/edit' was einen Parameter `id_s` erwartet. Also sowas wie '…/edit?id_s=13' als URL würde an diese Methode weitergeleitet.

Sprich ich muss eine Form definieren, die genauso funktioniert wie meine save-Variante und dann über GET die ID and die Action = "/edit" hängen, wenn ich das jetzt recht verstanden habe? Daraus dann ein Form gebastelt und einfach in der JSON-Datei an passender Stelle ersetzen. Dann müsste mich ja jeder Edit.Linkssprung ein Form generieren und das an der passenden ID ersetzen
BlackJack

@breedi: Ich meine auch die JSON-Datei, denn nur dort hast du ja Arrays, in Python gibt es keine Arrays, beziehungsweise verwendest Du in Deinem Programm nirgends welche. Die würden auch keinen Sinn machen. Aber verwirrenderweise steht ein `_a`-Suffix bei Namen an die *Listen* gebunden werden. Falls das für „array“ stehen sollte: Das ist kein Python, da schreibt und denkt jemand anscheinend in einer anderen Programmiersprache. Wir stellen halt ein Objekt mit Schlüsseln (Zeichenketten) die von der Semantik her eigentlich Indexwerte in ein Array sein könnten/sollten in Frage. Objekt und Array hier auf JSON bezogen. In Python sind das Wörterbuch und Liste.

Das Datenbankmodell hat irgendwie wenig mit den Daten in Deiner Anwendung zu tun‽ Das würde man doch mit mehr als einer Tabelle umsetzen müssen‽ Was sind denn die Anforderungen/Stories die das hat? Das kann doch nicht nur eine grosse zweidimensionale Tabelle in der Benutzeroberfläche sein? Welche Rollen bei den Benutzern gibt es?

Ich habe übrigens mittlerweile den wirklich einfachen Fehler gefunden. Die `edit()`-Methode wird nicht ”exposed”. Vergleich mal mit einer beliebigen anderen Methode die tatsächlich einen Decorator hat. Manchmal ist's bloss ein einzelnes kleines Zeichen. :-)
breedi
User
Beiträge: 10
Registriert: Freitag 28. Oktober 2016, 18:50

@BlackJack:

Du meinst sicherlich diesen Teil:

Code: Alles auswählen

    def save(self, **data_opl):
	#-------------------------------------------------------
	    # Sichern der Daten: aufgrund der Formularbearbeitung muss
		# eine vollständige HTML-Seite zurückgeliefert werden!
		
		# data_opl: Dictionary mit den gelieferten key-value-Paaren
		
		# hier müsste man prüfen, ob die Daten korrekt vorliegen!
		
	    id_s = data_opl["id_s"]
	    data_a =[ data_opl["firmenname"]
	    ,   data_opl["branche"]
	    ,   data_opl["taetigkeit"]
	    ,   data_opl["sitz"]
	    ,   data_opl["anzahl"]

		]
	    if id_s != "None":
		    # Update-Operation
		    self.db_o.update_px(id_s, data_a)
	    else:
			# Create-Operation
		    id_s = self.db_o.create_px(data_a)
			
	    return self.createForm_p(id_s)
		
    @cherrypy.expose
Dies ist die Datenstruktur aus seiner Vorlage mit der ich gearbeitet habe und diese habe ich auch weitesgehend übernommen. Mir war nicht bewusst das die Semantik aus einer anderen Sprache kommt. Aber Danke für den Hinweis.

Das ist nur ein Praxisphasenmanager, der in 2 Tabellen ausgelagert wird und ich dafür später 2 JSON-Datenquellen brauche. Es werden nur kleinere Überprüfungen gemacht, die man mit dem durchlaufen des JSON-Dateien Problemlos feststellen kann ( Hat der Student bereits einen Praxisphasenplatz etc, die sollen auch nicht als Fremdschlüssel implementiert werden ) Ich bin grade am Anfang, aber sobald ich verstanden habe wie ich das bearbeiten schreiben kann, wird die andere Tabelle auch kein Problem werden, schätze ich.
Ich habe übrigens mittlerweile den wirklich einfachen Fehler gefunden. Die `edit()`-Methode wird nicht ”exposed”. Vergleich mal mit einer beliebigen anderen Methode die tatsächlich einen Decorator hat. Manchmal ist's bloss ein einzelnes kleines Zeichen. :-)
Mir sagt das gerade nicht viel, und ich suche den Fehler schon sehr lange. Ich muss grade ersteinaml nachlesen was exposed / Decorator in diesem Bezug heißt :D
breedi
User
Beiträge: 10
Registriert: Freitag 28. Oktober 2016, 18:50

@BlackJack: Vielen Dank: Ich habe den Fehler gefunden. Ich bin dir zutiefst dankbar, ich habe Stunden damit verbracht das zu finden. Ich habe alles auf den Kopf gestellt und bin nicht zu einem Ergebnis gekommen, da es in der Theorie ja richtig geschrieben worden ist. So kleine Dinge sieht man auch wirklich nicht, wenn man nur mit dem Quellcode arbeitet sugeriert das Hirn ja auch manchmal das Dinge da sind, die eigentlich nichtvorhanden sind....

Ich muss zugeben, im Grunde genommen ist dies schon ein peinlicher kleiner Fehler :D :o


( Für alle die den Beitrag in ferner Zukunft lesen: In der Add-Funktion über dem Edit, fehlt das @-Zeichen vor dem cherrypy.expose und daher erkennt er die Editfunktion nicht)
BlackJack

@breedi: Vielleicht noch mal was zu HTTP: GET-Anfragen sollten eigentlich den Zustand auf dem Server nicht verändern. Das betrifft zum Beispiel '…/delete/<id>'. Wenn Du das Fenster schliesst und den Browser erneut startest und der die Sitzung mit dieser Seite wieder herstellt und dabei erneut die Seite abfragt wird wieder gelöscht, auch wenn das vielleicht gar nicht gewollt ist. Anders herum kann der Browser GET-Anfragen mit der gleichen URL Cachen, das heisst es kann Dir passieren das beim ersten mal der Datensatz gelöscht wird, aber bei folgenden Aufrufen einfach nur die Seite aus dem Browsercache angezeigt wird die beim ersten Löschen aktuell war. Die Daten auf dem Server können aber mittlerweile ganz andere sein und die Löschaktion wird nicht durchgeführt.

Also bei HTTP-GET nichts am Datenbestand ändern, sondern mindestens HTTP-POST dafür verwenden.

Diese Unterscheidung kann man auch in der selben Methode treffen. Also nicht '…/edit/13' nur um den Datensatz abzufragen und '…/save' um den Datensatz zu speichern, sondern beides bei '…/edit/13' erledigen. Je nach dem ob es ein GET oder ein POST auf diese URL war. Und bei '…/delete/13' nur löschen wenn es tatsächlich ein POST war. Bei GET entweder gar nichts machen oder eine Fehlermeldung liefern.

Alternativ könntest Du auch noch ein bisschen weiter gehen und eine RESTful-API implementieren. Also nur die URL '…/entry/13' und dann je nach dem ob es ein GET, POST, PUT, oder DELETE war, die entprechende Aktion durchführen. Die CherryPy-Dokumentation hat dazu ein Kapitel. Erfordert auf Browserseite dann allerdings ein bisschen JavaScript, weil man PUT und DELETE sonst nicht absetzen kann.

Edit: Zwei JSON-Dateien würde ich nur machen wenn deren Inhalte wirklich unabhängig voneinander sind. Datei(en) statt Datenbank ist ja sowieso schon unsicher wenn das mehr als einer gleichzeitig verwendet, aber dann auch noch riskieren das zwei Dateien zueinander inkonsistent werden können, das wäre doch etwas zu viel. Ich würde da auch gar nicht erst versuchen eine relationale Datenbank in JSON nach zu bauen. Solange sich die Daten komplett als Baum repräsentieren lassen, würde ich das genau so in einem JSON-Dokument speichern. Beispiel:
[codebox=javascript file=Unbenannt.js]{
"companies": {
"cia": {
"name": "CIA"
"phases": {
"spy": { // Praxisphase die noch keinen Studenten/Lehrenden hat.
"description": "Spionage",
"student": null,
"lecturer": null
},
"false_flag": {
"description": "'False-Flag' Operation",
"student": "O. b. Laden",
"lecturer": "G. W. Bush"
}
}
},
"bosch": {
"name": "Bosch"
"phases": {
"dieselgate": {
"description": "Akustikfunktionen kalibieren",
"student": "M. Mustermann",
"lecturer": "C. Heater"
}
}
}
}
}[/code]
Statt numerischer IDs gibt es hier kurze Text-IDs die man den Benutzer dann eingeben lassen kann. Die eignen sich auch für URLs. Unter '…/show' könnte man eine Übersicht der Firmen anzeigen. Unter '…/show/cia' dann die Praxisphasen von/bei der CIA, und unter '…/show/bosch/dieselgate' dann beispielsweise die Daten zur Praxisphase „Akustikfunktionen kalibrieren“ bei der Firma Bosch.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Alternativ könntest Du auch noch ein bisschen weiter gehen und eine RESTful-API implementieren
Genau die Idee hatte ich gestern Abend auch, als ich ein bisschen in der CherryPy Doku gelesen habe. Würde sich für diese Aufgabe sehr anbieten.

Gruß, noisefloor
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

BlackJacks Idee mal ganz simple mit bottle umgesetzt:

Code: Alles auswählen

#!/usr/bin/env python
import json
from bottle import run, request, redirect, template, Bottle

PORT = 3434
data = """{
  "companies": {  
    "cia": {
      "name": "CIA",
      "phases": {
        "spy": {
          "description": "Spionage",
          "student": null,
          "lecturer": null
        },
        "false_flag": {
          "description": "'False-Flag' Operation",
          "student": "O. b. Laden",
          "lecturer": "G. W. Bush"
        }
      }
    },
    "bosch": {
      "name": "Bosch",
      "phases": {
        "dieselgate": {
          "description": "Akustikfunktionen kalibieren",
          "student": "M. Mustermann",
          "lecturer": "C. Heater"
        }
      }
    }
  }
}"""

database = json.loads(data)
app = Bottle()

@app.get('/')
def show_all():
    return template("""<html><body>
    <h1>Firmen</h1>
    <table>
    % for name, info in companies:
        <tr><td><a href='/{{name}}/'>{{info['name']}}</a></td></tr>
    %end
    </table>
    </body></html>""", companies=database['companies'].items())

@app.get('/<company>/')
def show_company(company):
    company_info = database['companies'][company]
    return template("""<html><body>
    <h1>Stellen bei {{company_name}}</h1>
    <table>
    % for phase, info in phases:
        <tr>
            <td><a href='{{phase}}'>{{info['description']}}</a></td>
            <td>{{info['lecturer'] or '-'}}</td>
            <td>{{info['student'] or '-'}}</td>
        </tr>
    %end
    </table>
    </body></html>""", company_name=company_info["name"],
        phases=company_info['phases'].items())

@app.get('/<company>/<phase>')
def show_phase(company, phase):
    company_info = database['companies'][company]
    phase_info = company_info['phases'][phase]
    return template("""<html><body>
    <h1>Stelle {{info['description']}} bei {{company_name}}</h1>
    <form action="" method="post">
    Student: <input name="student" value="{{info['student'] or ''}}"><br/>
    Dozent: <input name="lecturer" value="{{info['lecturer'] or ''}}"><br/>
    <button>Speichern</button>
    </form>
    </body></html>""", company_name=company_info['name'],
        info=phase_info)

@app.post('/<company>/<phase>')
def post_phase(company, phase):
    company_info = database['companies'][company]
    phase_info = company_info['phases'][phase]
    phase_info['lecturer'] = request.POST['lecturer']
    phase_info['student'] = request.POST['student']
    return redirect("/{}/".format(company))

if __name__ == "__main__":
    run(app=app, host='', port=int(PORT))
breedi
User
Beiträge: 10
Registriert: Freitag 28. Oktober 2016, 18:50

Vielen Dank für die hilfreichen Antworten. In Zukunft orientiere ich mich an diese Frameworks sowie die Programmiereigenschaften. Morgen arbeite ich weiter am Projekt, da ich heute nicht allzu viel Zeit dafür hatte :)
Antworten