Probleme bei der Formularauswertung mit cgi

Django, Flask, Bottle, WSGI, CGI…
Antworten
Ditschi
User
Beiträge: 6
Registriert: Montag 13. April 2015, 07:17

Hallo

Ich habe ein Problem beim printen des html code nach der Formularauswertung.

In meiner index.html habe ich eine select-box mit folgender option:

Code: Alles auswählen

<option value=\x00\x00\x00\x00>00:00:00:00</option>
diesen Code kann ich dann beim Betrachten des Queltext nach Webseitenaufruf im html finden.

In Pyton möchte ich nun exakt diese Zeile in das zu erzeugende html dokument schreiben

Code: Alles auswählen

print '<option value=\x00\x00\x00\x00>00:00:00:00</option> '
wenn ich mir den erzeugten html code im browser ansehe wird allerdings nur

Code: Alles auswählen

<option value=>00:00:00:00</option>
angezeigt. (der Wert des Value fehlt)

Das Problem scheint der Wert \x00\x00\x00\x00 zu sein.

Ein zweiter Lösungsversuch war:
Die anderen Optionen des select erzeuge ich aus einer csv Datei. Dies funktioniert wie es soll, die Values werden im html angezeigt.

Code: Alles auswählen

print ''' <option value='''+ row[1] +'''>''' + row[0] + '''</option> '''
Die csv Datei erzeuge ich jedes mal nach der Formularauswertung mit den Werten aller select Boxen auf der Website. Auch dies funktioniert, ich lese den aktuellen Wert der Auswahlbox und daraus wird eine Zeile in der csv erzeugt (Werte in index.html sind korrekt):

Code: Alles auswählen

writer.writerow((address[2]+address[3]+":"+address[6]+address[7]+":"+address[10]+address[11]+":"+address[14]+address[15] , address ))
==>
11:22:33:44,\x11\x22\x33\x44
Da dieses automatische erzeugen der optionen aus der csv funktioniert wollte ich die Zeile
00:00:00:00,\x00\x00\x00\x00
beim Erstellen der csc hinzufügen:

Code: Alles auswählen

def known_addresses_save():
	try:
		csvfile = open("known_addresses.txt", "wb")	
		writer = csv.writer(csvfile)
		
		writer.writerow(("00:00:00:00" , "\x00\x00\x00\x00" )) # add default address  <<<<-----------

		# ... some more tasks...

	finally:
		csvfile.close()
Der writerow Befehl erzeugt allerdings nur ein
00:00:00:00,
in der csv Datei

wer kann mir hier helfen? Wie kann ich \x00\x00\x00\x00 als value der select-option setzen oder in die csv schreiben?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ditschi: Der Backslash ist in Python-Stringliteralen ein Sonderzeichen, Du mußt also den Backslash schützen, indem Du ihn verdoppelst "\\":

Code: Alles auswählen

print '<option value=\\x00\\x00\\x00\\x00>00:00:00:00</option> '
ganz allgemein ist das ein seltsamer String für ein value. Warum nimmst Du nicht einfach den Options-Text "00:00:00:00"?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Noch viel seltsamer (auch wenn es nur aus dem Titel und nicht dem Beitrag hervorgeht): Wieso nutzt Du plain CGI? Das ist in Python eher unüblich - nutze eine (WSGI) kompatibles Webframework wie Django, Flask oder Bottle. Und für Formulare gibt es auch *fertige* Validierungs-Frameworks, die Du bei den beiden letztgenannteren nutzen solltest; Django bringt das alles out-of-the box mit. Hier mal der Link zu WTForms, einem "Klassiker" :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Ditschi
User
Beiträge: 6
Registriert: Montag 13. April 2015, 07:17

DANKE Sirius3! das hat meine (beiden) Probleme gelöst.

Die Website ist lediglich für das versenden von Uart Befehlen mit einem Raspberry Pi ...
da war mit Django etc zu viel...

Vielen Dank für die schnelle Hilfe!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ditschi hat geschrieben: Die Website ist lediglich für das versenden von Uart Befehlen mit einem Raspberry Pi ...
da war mit Django etc zu viel...
Naja, da offenbar HTML-Formulare mit an Bord sind, scheint es ja doch mehr zu sein, als simples RPC... und da bietet sich ein Framework einfach an!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Ditschi: Ich kann ja verstehen das einem manchmal Django zu viel ist, aber Bottle oder Flask? Vor allem *was* zuviel? Mir wäre bei CGI die eigene Programmierarbeit deutlich zu viel. Bottle beispielsweise nimmt einem viel davon ab und besteht nur aus *einer* Python-Datei. Und da ist sogar eine kleine Template-Engine schon dabei.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ditschi: bei einem kleinen Projekt auf einem Raspberry wäre mir ein extra Apache-Server den ich aufsetzen muß, zu viel. Warum nimmst Du nicht ein Mikroframework inkl. Server wie Bottle oder Flask?
BlackJack

@Ditschi: Irgendwie komme ich nicht ganz mit Deinen Daten klar: Speicherst Du in der CSV-Datei tatsächlich immer zweimal den *gleichen* Wert nur unterschiedlich formatiert/repräsentiert? Warum? Und was machst Du an anderer Stelle im Programm mit der \x…-Darstellung? Wenn ich mir die Art wie Du Zeichenketten und Werte zusammen setzt anschaue ”rieche” ich da irgendwo ein `eval()`. Was gar nicht gut wäre.

Edit: Und wenn die Select-Boxen mit den Werten aus der CSV-Datei befüllt werden und dann die CSV-Datei wieder aus den Werten erstellt wird die der Benutzer dort ausgewählt hat, können a) weniger unterschiedliche Werte geschrieben werden, also im nächsten Durchgang weniger zur Auswahl stehen, weil der Benutzer ja b) Werte mehrfach in den unterschiedlichen Select-Boxen verwenden kann.
Ditschi
User
Beiträge: 6
Registriert: Montag 13. April 2015, 07:17

ok ... ich war zu faul mich in ein framework einzuarbeiten und dachte ich bin schneller wenn ich das einfach kurz mal codiere... jaja .. ggf. nicht die einfachste Lösung.. auf jeden Fall geht es jetzt..

habe nach den ersten Tests noch ein wenig umgestellt. ich erzeuge die select jetzt aus einer nicht veränderlichen Datei, damit habe ich immer alle Werte.

es ist richtig, dass ich die gleichen Daten in unterschiedlicher Formatierung im csy habe... so kann ich einfach die werte in das select einfügen.

die Values brauche ich um Daten mit pyserial zu versenden..

danke für die Hilfe!
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ditschi: Und wie bekommst Du die Daten von diesem seltsamen Format "\x12\x34\x56\x78" in Daten, die Du über pyserial versenden kannst? :twisted:
Genau, da Du sowieso einen Umwandlungsschritt brauchst, kannst Du gleich das viel bessere Format "12:34:56:78" verwenden und brauchst die Daten nur einmal zu speichern!
Ditschi
User
Beiträge: 6
Registriert: Montag 13. April 2015, 07:17

Wie ich zu \x12\x34\x56\x78 komme:
bei meinem aller ersten Test habe ich die adresse aus 4 textfeldern ausgelesen und erfolgreich mit pyserial gesendet.

Code: Alles auswählen

address=chr(int(form.getvalue('new_addr0'),16))+chr(int(form.getvalue('new_addr1'),16))+chr(int(form.getvalue('new_addr2'),16))+chr(int(form.getvalue('new_addr3'),16))
Das ganze hat "\x12\x34\x56\x78" ergeben (Speichern in datei) und ich konnte es mit pyserial senden

Code: Alles auswählen

ser.write(message) #message = address+"lalala"
daher habe ich "\x12\x34\x56\x78" als Value des select gewählt .. testen, ob das senden funktioniert konnte ich leider noch nicht, da der PI zu Hause steht...
Ditschi
User
Beiträge: 6
Registriert: Montag 13. April 2015, 07:17

Wie würdet ihr das denn besser lösen?

In der Auswahlbox soll eine Adresse in leserlicher Form (byteweise mit : getrennt) in hex dargestellt werden.
Zum Senden mit PySerial brauche ich die hex Werte gewählte Adresse.
BlackJack

@Ditschi: Das mit den Werten in der Datei wird nicht ohne weiteres funktionieren weil in der Datei die Zeichenkettenrepräsentation einer Zeichenkette steht die für den Programmierer zur Fehlersuche gedacht ist und nicht zum weiterverarbeiten im Programm. Wenn in der Datei \x12\x34\x56\x78 steht, man das also *so* in einem Texteditor lesen kann und jedes der 16 Zeichen ändern kann, dann hast etwas falsch gemacht. Den Wert muss man, wenn man das als 4 Bytes über eine serielle Schnittstelle schicken möchte, ja wieder von den 16 Zeichen in 4 Bytes umwandeln. Und da ist die \x-Darstellung nicht das mittel der Wahl sondern dafür nimmt man etwas aus dem `binascii`-Modul aus der Standardbibliothek. Zum beispiel `a2b_hex()` und `b2a_hex()`.

Bleibt noch die Frage ob die andere Spalte jeweils den gleichen Wert bloss in anderer Darstellung enthält und daran schliesst sich ein „Warum‽” an. Redundante Daten zu speichern ist oft eine schlechte Idee. Gerade bei einer Textdatei die man auch mal so ändern kann, ohne ein spezielles Programm, handelt man sich damit schnell Inkonsistenzen ein.

Besser gelöst wäre das wenn man in der Datei zum Beispiel die Hexdarstellung speichert die mit `binascii` erzeugt wurde und das als Wert für die Option verwendet. Denn daraus lassen sich leicht die anderen beiden Formen erzeugen — mit `binascii` die 4 Bytes und durch einfügen von ':' alle zwei Zeichen die Darstellung für den Nutzer. Wenn man mag kann man das noch in einen eigenen `Address`-Datentyp verpacken.
BlackJack

Vielleicht mal ein kleines Beispiel mit Bottle und der dort eingebauten SimpleTemplate-Engine:

``app.py``:

Code: Alles auswählen

#!/usr/bin/env python
# Start test server: python -m bottle --debug --reload app
import csv
from binascii import a2b_hex, b2a_hex
from bottle import request, route, view


class Address(object):
    def __init__(self, bytes='\x00\x00\x00\x00'):
        self.bytes = bytes

    def __str__(self):
        result = b2a_hex(self.bytes)
        return ':'.join(result[i:i + 2] for i in xrange(0, len(result), 2))

    @classmethod
    def from_string(cls, string):
        return cls(a2b_hex(string.replace(':', '')))


@route('/', method=['GET', 'POST'])
@view('index.html')
def index():
    if request.method == 'POST':
        addresses = map(
            Address.from_string, request.params.getall('addresses')
        )
        with open('addresses.csv', 'wb') as csv_file:
            csv.writer(csv_file).writerows([a] for a in addresses)
    else:
        with open('addresses.csv', 'rb') as csv_file:
            addresses = [
                Address.from_string(row[0]) for row in csv.reader(csv_file)
            ]
    return {'addresses': addresses}
``views/index.html``:

Code: Alles auswählen

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Adressen</title>
</head>
<body>
  <h1>Adressen</h1>
  <form method="POST">  
    <ol>
    % for selected_address in addresses:
      <li>
        <select name="addresses">
        % for address in addresses:
          <option
              value="{{address}}"
              {{!'selected' if address == selected_address else ''}}>
            {{address}}
          </option>
        % end
        </select>
      </li>
    % end
    </ol>
    <input type="submit" value="Speichern">
  </form>
</body>
</html>
``adresses.csv``:

Code: Alles auswählen

de:ad:be:ef
12:34:56:78
00:00:00:00
Edit: Wobei ich die Logik so implementiert habe wie ich Deine Beschreibung verstanden habe, allerdings habe ich den Sinn nicht verstanden.
Ditschi
User
Beiträge: 6
Registriert: Montag 13. April 2015, 07:17

Wow Vielen dank! Ich werde mir das Am Wochenende mal in Ruhe ansehen.


Habe meinen Bereits existierenden Code mit binascii und einer csv ohne doppelte Werte zum laufen bringen können. Funktionieren tut nun alles .. Allerdings ist nicht alles optimal.

Ich denke mit deinem Beispiel kann ich da noch einiges optimieren! Danke!
Die Umstellung der Logik bekommen ich hin ;)
Antworten