HTML Form Werte in JSON Datei per Python 3 Funktion speichern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Jack93
User
Beiträge: 2
Registriert: Donnerstag 25. Oktober 2018, 17:03

Hallo,

ich muss für ein Projekt eine HTML Form anlegen und die dort eingetragenen Werte, nachdem ich auf den Submit Button gedrückt habe, in eine JSON Datei speichern. Dabei darf ich kein Framework oder ähnliches verwenden außer CherryPy.

HTML:

Code: Alles auswählen

<!DOCTYPE html>
<html>
<body>

<h2>Kunde anlegen</h2>

<form action="/saveCustomer">
  Vorname:<br>
  <input type="text" name="kundenvorname" value="Vorname">
  <br>
  Nachname:<br>
  <input type="text" name="kundennachname" value="Nachname">
  <br>
 Anschrift:<br>
  <input type="text" name="kundenanschrift" value="Anschrift">
  <br>
 Bezeichnung:<br>
  <input type="text" name="kundennummer" value="Kundennummer">
  <br><br>
  <input type="submit" value="Submit">
</form> 


</body>
</html>
Database.py:
...mehr Code...

Code: Alles auswählen

    def saveCustomer(self):
        newKunde = parse_query_string(cherrypy.request.query_string)
        with open('kunden.json', 'w') as outfile:
            json.dump(newKunde, outfile)
Das funktioniert aber nicht. Ich bekomme schon einen Fehler sobald ich auf den Submit Button drücke, dass der dort angegebene Link nicht gefunden werden kann, logisch. Aber wie kann ich dort denn eine konkrete Funktion aus meiner database.py aufrufen und anschließend die im HTML Form eingetragenen Werte korrekt in einer JSON Datei speichern?

Den Weg anders herum, wenn ich per Hand was in die JSON Datei eintrage und diese per load in Python ausgebe, funktioniert.

Grüße!
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Frage wird hier direkt am Anfang beantwortet:
http://docs.cherrypy.org/en/latest/tutorials.html

Du brauchst auch den Query-String nicht selbst zu parsen, sondern baust einfach die passenden Argumente ein. Oder alternativ ginge auch **kwargs in der Funktionssignatur.
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie sieht denn Dein gesamter Code aus? Wo ist das `@cherrypy.expose`?

Variablen und Methoden werden klein_mit_unterstrich geschrieben. Wenn man ein Kundenobjekt hat, dann ist das ‹kunden› bei den Schlüsseln relativ redundant. Niemals Daten, die von extern kommen, ungeprüft weiterverarbeiten. Jeder kann beliebige Schlüssel in die Datei „kunden.json” schreiben. Damit kann man potentiell Fehler in anderen Teilen, die die Daten weiter verarbeiten, ausnutzen.

Formulare sollten per POST-Request verschickt werden.
Jack93
User
Beiträge: 2
Registriert: Donnerstag 25. Oktober 2018, 17:03

Hallo Jungs,

danken für die Antwort und Erklärung!

Hallo Jungs und vielen Dank für eure Antwort sowie Erklärung.
snafu hat geschrieben: Donnerstag 25. Oktober 2018, 17:52 Die Frage wird hier direkt am Anfang beantwortet:
http://docs.cherrypy.org/en/latest/tutorials.html
Bezieht sich dies auf meine Frage, wie ich den Link beziehungsweise die Funktion als action korrekt angebe, ergo auf Tutorial 4 in den CherryPy Tutorials? Diese habe ich vor ca. 1.5 Wochen durchgearbeitet und den Link ja auch dementsprechend gesetzt (siehe meinen Ausgangspost). Habe dabei sowohl <form action="/saveCustomer"> als auch die Version ohne Backslash vor dem Funktionsnamen gesetzt.

Drücke ich dann auf den Submit Button, spuckt mir CherryPy leider immer folgenden Fehler.

Code: Alles auswählen

The path '/saveKunde' was not found.
………
cherrypy._cperror.NotFound: (404, "The path '/saveKunde' was not found.")
Er hat application.py ja auch als Ausgangspunkt, heißt er findet die Methode saveKunde ja nicht, da sind in database.py ist. Müsste er an sich ja auch nicht, da die Funktion ja nur im Hintergrund arbeitet und er anschließend zum Beispiel zur Kundenübersicht zurückkehren kann. Bin grad aber irgendwie doch sehr überfragt, wie ich dies bewerkstelligen soll, da noch recht neu in der Materie.
snafu hat geschrieben: Donnerstag 25. Oktober 2018, 17:52 Du brauchst auch den Query-String nicht selbst zu parsen, sondern baust einfach die passenden Argumente ein. Oder alternativ ginge auch **kwargs in der Funktionssignatur.
Mit **kwargs sollte ich die einzelnen Key:Value Paare aus der URL/URI dann einfach per Index-Operator [] ansprechen können und diese dann mit JSON.dump speichern, korrekt? Ich denke die Variante mit dem Einbauen der Argumente ist schöner, wüßte grad allerdings noch nicht genau, wie das im Code aussehen würde. Das teste ich, sobald die Verarbeitung mit dem action korrekt funktioniert.
Sirius3 hat geschrieben: Donnerstag 25. Oktober 2018, 18:02 Wie sieht denn Dein gesamter Code aus? Wo ist das `@cherrypy.expose`?
Im Folgenden mal mein gesamter, relevanter Code. Durch das Copy und Paste sind die Einrückungen irgendwie verloren gegangen, der Code ist aber im Original korrekt formatiert.

Application.py:

Code: Alles auswählen

from pathlib import Path
import cherrypy

from . import view
from . import database


class Application:
    
    def __init__(self):
        self.views = view.View_cl(Path().absolute())
        self.database = database.Database_cl()
    
    @cherrypy.expose
    def index(self):
        return self.views.getIndex()
        
     @cherrypy.expose
    def Kunden(self):
        data = self.database.getKunden()
        return self.views.getKundenListe(data)
        
            @cherrypy.expose
    def createKunde(self):
        data = {}
        return self.views.createKunde(data)  
Databse.py:

Code: Alles auswählen

import os
import codecs
import json
from cherrypy.lib.httputil import parse_query_string

class Database_cl:
    def __init__(self):
        self.pathToData = "Der Pfad zu den Daten"
        self.pathToKunden = os.path.join(self.pathToData, "Kunden")
        self.kunden_data = {}
        
    def saveKunde(self): #Das wäre dann ja noch nicht korrekt, da ich der Methode ja entweder die Argumente direkt übergebe oder es per **kwargs löse
        newKunde = parse_query_string(cherrypy.request.query_string)
        with open('kunden.json', 'w') as outfile:
            json.dump(newKunde, outfile)        
View.py (Hier wird nur das entsprechende Mako-Template aufgerufen. Die entsprechenden Methoden create etc. waren vorgegeben):

Code: Alles auswählen

   
    def createKunde(self, data):
        return self.create('createKunde.tpl', data)  
Das Formular ist ja im Ausgangspost zu finden. Danke für die restlichen Hinweise hinsichtlich der Konventionen!

Lieben Gruß und ein angenehmes Wochenende!
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Das ist alles sehr kompliziert und verschachtelt geschrieben (kommt wahrscheinlich daher, dass CherryPy als Framework schon recht seltsam ist).

Auch wenn es sich für den Anfang etwas übertrieben anhört, halte Dich an die Namenskonventionen. Das hilft Dir Code besser zu lesen, und verhindert manche Fehler. Variablen und Funktionen werden klein_mit_unterstrich geschrieben. Klassen Gross. Dadurch ist klar, was eine Klasse ist und man muß kein unsinniges Kürzel wie _cl an jede Klasse hängen.

Klassen sollten eine klare Funktion haben. Bisher erkenne ich an der View-Klasse noch keinen Sinn. Alle Views von Application nach View durchzureichen ist jedenfalls nicht sinnvoll. Das kann direkt in Application geschrieben werden.

Die Database-Klasse dagegen, sollte nichts von einem Webframework wissen müssen. ›saveKunde‹ muß also schon eine geparsten Struktur von der entsprechenden Methode in Application bekommen.

Eine Datei die ›kunden.json‹ heißt, aber nur einen Kunden enthält, ist irreführend, entweder das eine oder das andere ist falsch.

pathToData sollte entweder eine KONSTANTE sein, oder ein Argument von Database.__init___, aber nicht als literaler String in __init__ stehen; so wie jetzt ist es sehr umständlich, diesen Pfad anzupassen.

Der Parameter zur View_cl ist auch falsch. Das soll duch wahrscheinlich der Pfad sein, in dem die Templates liegen, Du übergibst aber den aktuellen Pfad. Pfade relativ zum Modul, das gerade benutzt wird, schreibt man z.B. so:

Code: Alles auswählen

BASE_PATH = Path(__file__).parent
TEMPLATE_PATH = BASE_PATH / 'templates'
Jede Route muß in ›Application‹ angelegt werden. Das CherryPy weiß ja nicht, dass es irgendwo eine Database-Klasse gibt und dort ein SaveKunde.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Sirius3 hat geschrieben: Donnerstag 25. Oktober 2018, 18:02 Formulare sollten per POST-Request verschickt werden.
Dies nicht zu tun könnte dazu führen dass Kundendaten unnötig in Access Logs auftauchen, was aus DSGVO Sicht problematisch sein könnte.
Antworten