Seite 1 von 1

Einsetzen von Bytestring (utf-8) in Unicodestring

Verfasst: Donnerstag 4. Juni 2009, 16:11
von filchos
Hallo,

Vorgeschichte

Ich benutze Django mit DEFAULT_CHARSET = 'utf-8'. Die Modelle geben für die Werte utf-8-codierte Bytestrings zurück. Soweit OK, obwohl mir Unicodestrings lieber wären.

Problem

Ich möchte einen als utf-8 codierten Bytestring in einen Unicodestring einsetzen:

Code: Alles auswählen

# -*- coding: utf-8 -*-
# doesn't work:
result = u'%s ist böse' % name_as_utf8_bytestring
In diesem Falle bekomme ich einen UnicodeDecodeError zurück, sobald name_as_utf8_bytestring Nichtasciizeichen enthält.

Eine mögliche Lösung wäre:

Code: Alles auswählen

# -*- coding: utf-8 -*-
name_as_unicodestring = name_as_utf8_bytestring.decode('utf-8')
result = u'%s ist böse' % name_as_unicodestring
Das macht den Code aber aufgeblähter, da solche %-Operationen laufend vorkommen.

Frage

Geht das auch einfacher bzw. gibt es hier eine Best Practice?

Schöne Grüße,
Olaf[/code]

Verfasst: Donnerstag 4. Juni 2009, 16:16
von cofi
Statt

Code: Alles auswählen

name_as_unicodestring = name_as_utf8_bytestring.decode('utf-8')
result = u'%s ist böse' % name_as_unicodestring
kannst du

Code: Alles auswählen

result = u'%s ist böse' % name_as_utf8_bytestring.decode('utf-8')
benutzen.
Eventuell gibt es bei der neuen `format` Methode noch was schickeres, aber da bin ich noch nicht richtig fit.

Verfasst: Donnerstag 4. Juni 2009, 16:27
von lunar
Als Anmerkung: Die Best Practice ist sicherlich, intern ausschließlich mit unicode-Objekten zu arbeiten, und die Kodierung nur bei der Ein- und Ausgabe vorzunehmen. Soll heißen: Wenn du in die Verlegenheit kommst, Unicode-Zeichenketten und Bytefolgen konkatenieren zu müssen, hast du vorher bereits was falsch gemacht.

Verfasst: Donnerstag 4. Juni 2009, 16:50
von filchos
@lunar

… das heißt, ich müsste schauen, ob ich an dem Defaultverhalten von Django, Modellattribute als utf-8-codierte Bytestrings zurückzugeben, etwas ändern kann. Richtig?

Grüße,
Olaf

Verfasst: Freitag 5. Juni 2009, 09:29
von lunar
Diese Frage müssen dir Django-Experten beantworten ... ich kenne mich mit Django's ORM nicht wirklich aus. Im Allgemeinen aber sollte die Daten bzw. das Modell dir Unicode-Objekte zurückliefern.

Verfasst: Freitag 5. Juni 2009, 19:16
von sma
filchos hat geschrieben:… das heißt, ich müsste schauen, ob ich an dem Defaultverhalten von Django, Modellattribute als utf-8-codierte Bytestrings zurückzugeben, etwas ändern kann. Richtig?
Django 1.x gibt Attribute nicht als UTF-8-kodierte Bytes zurück, sondern als Unicode-Strings. Dein Fehler muss irgendwo anders liegen. Das DEFAULT_CHARSET hat nur dem Erzeugen von Webseiten (und möglicherweise dem Einlesen von Formularen) zu tun, nicht aber damit, wie Modell in der Datenbank verarbeitet werden.

Stefan

Verfasst: Mittwoch 10. Juni 2009, 16:59
von filchos
Hm,

dann habe ich irgendeinen Fehler in den Konfigurationen. Die Modelle geben mir die Attribute als String zurück:

Code: Alles auswählen

from myapplication.user.models import User
user = User.objects.get(id=1)
print type(user.displayname)

# -> <type 'str'>
Woran kann das sonst liegen?

Grüße,
Olaf

P.S.: einige Konfigurationsdaten:


Python-Version: 2.5.1
Django-Version: 1.0.2
System: Mac OS X 10.5.7
Django wird mit ./manage.py runserver gestartet (Testsystem)

Datenbank: MySQL 5.0.67
Tabellen: InnoDB (Collaction utf8_bin)

SELECT hex( "ö" ) gibt mir brav C3B6 zurück, also läuft die DB mit utf-8.

locale (auf der Konsole) gibt:

Code: Alles auswählen

LANG="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_CTYPE="de_DE.UTF-8"
LC_MESSAGES="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_ALL=

Verfasst: Mittwoch 10. Juni 2009, 17:16
von BlackJack
@filchos: Wie ist denn das Modell an der Stelle definiert?

Und Deine Aussage über das SELECT ist zu einfach. Das "ö" liegt im Skript in irgendeiner Kodierung vor, wird durch die Datenbankanbindung zum Server geschleust, und dort dann umgewandelt. Daraufhin jetzt zu sagen die DB arbeitet mit UTF-8 ist unseriös. Die könnte auch mit ISO-8859-15 arbeiten und hat halt zwei Zeichen bekommen weil das "ö" eben UTF-8 kodiert war bevor es zur DB geschickt wurde.

Verfasst: Mittwoch 10. Juni 2009, 17:30
von filchos
@Blackjack:

Stimmt, mit dem „ö-Test“ war ein Schnellschuss, der nicht aussagekräftig ist.

Ein Auszug aus dem Modell:

Code: Alles auswählen

# -*- coding: utf-8 -*-
from django.db import models

class User(models.Model):

    class Meta:
        db_table = 'user'

    firstname   = models.CharField(max_length = 50, blank=True, verbose_name='Vorname')
    lastname    = models.CharField(max_length = 50, blank=True, verbose_name='Nachname')
    displayname = models.CharField(max_length = 50, blank=True, verbose_name='Anzeigenname')

    # and a lot of other fields

    def name(self):
        if self.displayname:
            return self.displayname
        else:
            return  u'%s %s' % (self.firstname, self.lastname)

    def __unicode__(self):
        return self.name()
    
    def __repr__(self):
        return u'<User #%i (%s)>' % (self.id, self.name())

    # and a lot of other methods

Verfasst: Mittwoch 10. Juni 2009, 18:17
von filchos
Nachtrag, mehr spaßeshalber:

select hex('中') gibt als Antwort: E4B8AD

Das entspricht der utf-8-Kodierung des Unicode-Codepoints 4E2D, der auch tatsächlich dem Zeichen 中 entspricht.

Grüße,
Olaf

Verfasst: Montag 15. Juni 2009, 15:37
von filchos
Ich werde jetzt versuchen, im Djangocode selber die Ursache zu finden.

Ergebnisse gebe ich ans Forum zurück, über Hilfe freue ich mich aber weiterhin ;-).

Grüße,
Olaf

Verfasst: Montag 15. Juni 2009, 17:28
von filchos
Zum Stand:

ich habe mich einmal quer durch Django durchgewühlt und komme zu folgendem Ergebnis:

Ich rufe eine Seite mit u.a. folgendem Testcode auf:

Code: Alles auswählen

u = User.objects.get(id=45)
In django/db/models/sql/query.py habe ich in der Methode execute_sql der Klasse BaseQuery folgenden Code eingefügt:

Code: Alles auswählen

        # vorhandener Code
        cursor = self.connection.cursor()
        cursor.execute(sql, params)
        # eingefügte Exception (Textfeld an Position 5)
        dn = type(cursor.fetchone()[5])
        raise Exception(dn)
        # wieder vorhandener Code
        if not result_type:
Ich erhalte: <type 'str'> als Ausgabe. Dieser String ist utf-8-codiert.

Folgende Fehlermöglichkeiten gibt es jetzt meiner Meinung nach:

· Hier sollte schon <type 'unicode'> zurückkommen, <type 'str'> wird nur fälschlicherweise zurückgegeben.
· Hier kommt richtigerweise <type 'str'> zurück, aber die Unicodeumwandlung auf höherer Ebene funktioniert nicht.
· Die Aussage, dass die Felder per default als Unicodestrings zurückkommen ist falsch.

Kann mir hier jemand helfen, wo hier der Fehler steckt?

Danke und schöne Grüße,
Olaf

P.S.: @Administratoren: Gibt es eine Möglichkeit, diesen Eintrag in das Forum „Web- und Netzwerkprogrammierung“ umzuziehen? Das Problem hat sich ja im Laufe der Diskussion verlagert.

Verfasst: Montag 15. Juni 2009, 18:00
von filchos
Heureka,

ich habe es vermutlich gefunden!:

Ich hatte bei MySQL als Collation utf8_bin angegeben. In diesem Fall werden tatsächlich Bytestrings zurückgegeben. Siehe auch:

http://docs.djangoproject.com/en/dev/re ... n-settings

Bei Verwendung von utf8_swedish_ci werden – endlich – wie von mir erwartet Unicodestrings zurückgegeben.

Fazit: Das Verhalten war zwar dokumentiert, aber es hat doch gedauert, bis ich es gefunden habe.

Grüße,
Olaf