Django -> suds UTF-8 / Uncode Problem

Django, Flask, Bottle, WSGI, CGI…
Antworten
Benutzeravatar
Damaskus
Administrator
Beiträge: 995
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Hallo Community,

ich stecke gerade an einem Coding Problem fest.
Ausgang ist eine Django Installation, die Daten per Input von einem HTML-Formular erhält.
In der Preview Page und auch wenn ich die Daten per SQL direkt in die DB schreiben behalten ihre UTF-8 Codierung, Python Intern werden die Zeichen auch korrekt in Unicode verarbeitet.

Sobald ich aber die Daten über den Suds-SOAP-Client verschicke habe ich das Problem, dass alle nicht ASCII Zeichen in ein "?" umgewandelt und in die DB geschrieben werden. Wenn ich Daten aus der DB abrufe erhalte ich alle richtig codiert.

Der SOAP-Server arbeitet (korrekt!) in UTF-8 und erwartet die Daten auch UTF-8 kodiert.

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<address xsi:type="xsd:string">Gro?melt</address>
<zip xsi:type="xsd:string">0123456</zip>
<city xsi:type="xsd:string">?lbro</city>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
Mehr Hinweise oder Fehlermeldungen habe ich leider nicht.
Hat jemand Ideen oder Vorschläge wie ich dem Problem genauer auf den Zahn fühlen kann?

Gruß
Damaskus
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

irgendwo wird wohl eine Umwandlung mit error="replace" gemacht. Durchsuche doch mal den sourcecode....

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Damaskus hat geschrieben: Sobald ich aber die Daten über den Suds-SOAP-Client verschicke habe ich das Problem, dass alle nicht ASCII Zeichen in ein "?" umgewandelt und in die DB geschrieben werden. Wenn ich Daten aus der DB abrufe erhalte ich alle richtig codiert.
Woher kommen die Daten?
Benutzeravatar
Damaskus
Administrator
Beiträge: 995
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

apollo13 hat geschrieben:Woher kommen die Daten?
Die Daten kommen von der SOAP Schnittstelle (es geht um Adressen aus einer Kunden-DB) in UTF-8 codiert und werden direkt in ein Form-Set übergeben:

Code: Alles auswählen

#----------------------------------------------------------------------
@csrf_protect
@login_required
def kedit(request):
    """"""

    # Daten per SOAP abrufen
    gsCustomer = settings.GS_API_CLIENT.service.getCustomer(
        settings.SECRET_KEY,
        int(request.user.username)
    )
    
    data = {
        'company':gsCustomer.result.company,
        'typ':gsCustomer.result.typ,
        'address':gsCustomer.result.address,
        'plz':gsCustomer.result.zip,
        'city':gsCustomer.result.city,
        'country':gsCustomer.result.country,
    }
    form = myform.KdataEditForm(data)

    return render_to_response(
        'mydata/kdata_edit.html',
        {
            'form':form,
            'customer':gsCustomer,
        },
        context_instance=RequestContext(request)
    )
hier können die Daten editiert werden und gehen danach durch die Preview Funktion von Django.
Und werden dann wieder per SOAP zurück an die DB übergeben.

Code: Alles auswählen

########################################################################
class KundendatenEditPreview(FormPreview):
    """"""

    preview_template = 'mydata/kundendaten_edit_preview.html'
    form_template = 'mydata/kundendaten_edit.html'

    #----------------------------------------------------------------------
    def done(self, request, cleaned_data):
        """"""
        
        gsCreateUpdateCustomer.company = cleaned_data['company']
        gsCreateUpdateCustomer.address = cleaned_data['address']
        gsCreateUpdateCustomer.zip = cleaned_data['plz']
        gsCreateUpdateCustomer.city = cleaned_data['city']

        gsUpdateCustomer = settings.GS_API_CLIENT.service.updateCustomer(
            settings.SECRET_KEY,
            int(request.user.username),
            gsCreateUpdateCustomer,
        )        

        return HttpResponseRedirect('/mydata/kundendaten')
Wie schon geschrieben sind die Daten im Preview noch korrekt und erst nach dem absenden über Suds werden die Daten geändert.
Ich bin mir inzwischen nicht mehr ganz sicher, aber wenn ich Daten von cleaned_data bekomme sollten diese doch schon per UTF-8 Codiert sein, richtig?

Gruß
Damaskus
BlackJack

@Damaskus: Da haben wir wohl auch schon das Problem:

Code: Alles auswählen

In [25]: f = forms.CharField()

In [26]: f.clean('test')
Out[26]: u'test'

In [27]: f.clean('täst')
Out[27]: u't\xe4st'
Wieso hättest Du da jetzt UTF-8 Bytestrings erwartet und keine *richtigen* Zeichenketten?
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Code: Alles auswählen

    form = myform.KdataEditForm(data)
Das ist eigentlich schon falsch, damit erzeugst du nen bound form, für sowas solltest du initial=data verwenden, der erste Parameter ist eigentlich nur für request.GET/POST gedacht. Wenn data selbst Fehler hat wird dir ohne initial das sofort bei der Anzeige angezeigt und nicht erst nach der Validation.
Wie schon geschrieben sind die Daten im Preview noch korrekt und erst nach dem absenden über Suds werden die Daten geändert.
Ich bin mir inzwischen nicht mehr ganz sicher, aber wenn ich Daten von cleaned_data bekomme sollten diese doch schon per UTF-8 Codiert sein, richtig?
Django ist unicode aware, sprich es erwartet von dir Unicode und gibt dir Unicode, das gilt sowohl für dein data zu Beginn, als auch das cleaned_data am Schluss.

lg,
apollo13
Benutzeravatar
Damaskus
Administrator
Beiträge: 995
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Nachdem ich das Thema eine Weile ruhen hab lassen (aus Zeitmangel) ist mir heute bei einer Code-Revision der Fehler aufgefallen...

aber erstmal:
apollo13 hat geschrieben:

Code: Alles auswählen

    form = myform.KdataEditForm(data)
Das ist eigentlich schon falsch, damit erzeugst du nen bound form, für sowas solltest du initial=data verwenden, der erste Parameter ist eigentlich nur für request.GET/POST gedacht. Wenn data selbst Fehler hat wird dir ohne initial das sofort bei der Anzeige angezeigt und nicht erst nach der Validation.
Stimmt, Danke für den Hinweis! Ist natürlich falsch das Form mit data als Argument zu initialisieren.

Und natürlich verwendet Django Unicode und NICHT utf-8, hatte da einen etwas dicken Kopf vor lauter Fehlersuche...

Die eigentliche Lösung des Problems liegt im HTML versteckt:
Während ich bei dem Formular noch brav

Code: Alles auswählen

<form action="/mydata/kundendaten/post" method="post" accept-charset="utf-8">
verwendet hab. War ich bei dem Preview etwas schlampig und hab nur

Code: Alles auswählen

<form action="" method="post">
geschrieben.

Somit lag der Fehler nicht am SUDS-Client oder Django sondern eben ganz einfach beim Encoding der POST Daten.

Danke an alle fürs Fehler suchen :)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Aber ist accept-charset="utf-8" wirklich nötig, wenn die Seite als UTF-8 gesendet wurde?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
frabron
User
Beiträge: 306
Registriert: Dienstag 31. März 2009, 14:36

User agents may interpret this value as the character encoding that was used to transmit the document containing this FORM element.
Von hier. Meiner Meinung nach ist das Browserabhängig und man sollte, um sicher zu gehen, immer ein accept-charset angeben, da dies nur eine optionale Regel ist.
Benutzeravatar
Damaskus
Administrator
Beiträge: 995
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Eigentlich müsste doch im Header ein

Code: Alles auswählen

<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

ausreichen.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

das vermute ich auch. Am besten dazu noch im html header:

Code: Alles auswählen

Content-Type	text/html; charset=UTF-8

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Damaskus hat geschrieben:Eigentlich müsste doch im Header ein

Code: Alles auswählen

<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

ausreichen.
Ich habe noch nie diese meta Headers oder accept-charset verwendet und mit keinem einzigen Browser jemals ein Problem gehabt, was für kaputte Software hast du? ;)
Benutzeravatar
Damaskus
Administrator
Beiträge: 995
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Django über mod_wsgi auf einem Debian Host der über SUDS mit einem PHP NuSOAP Server spricht ;)
Ich würde ja sehr gerne den ganzen SOAP / PHP Schrott weg lassen, aber das geht leider nicht.

Wobei mir der SUDS-Client am meisten Probleme gemacht hat, da es für Python (meiner Meinung nach) noch immer keinen wirklich brauchbaren SOAP Client gibt.
Zu SUDS welches ja anscheinend nicht mehr aktiv weiter entwickelt wird, gibts inzwischen einen Fork Namens jurko-suds.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Dass die Entwickler auf SOAP keine Lust haben ist irgendwie verständlich ;)
lunar

Ein brauchbarer Client für ein unbrauchbares Protokoll wäre auch ein ziemlicher Wiederspruch ;)
Benutzeravatar
Damaskus
Administrator
Beiträge: 995
Registriert: Sonntag 6. März 2005, 20:08
Wohnort: Schwabenländle

Damaskus hat geschrieben:Eigentlich müsste doch im Header ein

Code: Alles auswählen

<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

ausreichen.
Noch ein allerletzter Nachtrag zu dem Thema: :wink:

Code: Alles auswählen

<meta charset="utf-8">
Wird natürlich vom W3C Validator bemängelt und hat im HTML nichts verloren.

Nachdem ich dem Fehler doch nochmal verfolgt habe, ist es jetzt final geklärt.
Der SOAP Server bzw. die Anwendung hinter dem SOAP Server hat tatsächlich willkürlich einige Antworten nicht UTF-8 kodiert ausgegeben.

Danke an alle die geholfen haben den Fehler zu suchen :D

Gruß
Damaskus
Antworten