it-swarm.com.de

MySQL-Fehler "Fehlerhafter Stringwert" beim Speichern des Unicode-Strings in Django

Ich habe eine seltsame Fehlermeldung erhalten, als versucht wurde, first_name, last_name in Djangos auth_user-Modell zu speichern.

Fehlgeschlagene Beispiele

user = User.object.create_user(username, email, password)
user.first_name = u'Rytis'
user.last_name = u'Slatkevičius'
user.save()
>>> Incorrect string value: '\xC4\x8Dius' for column 'last_name' at row 104

user.first_name = u'Валерий'
user.last_name = u'Богданов'
user.save()
>>> Incorrect string value: '\xD0\x92\xD0\xB0\xD0\xBB...' for column 'first_name' at row 104

user.first_name = u'Krzysztof'
user.last_name = u'Szukiełojć'
user.save()
>>> Incorrect string value: '\xC5\x82oj\xC4\x87' for column 'last_name' at row 104

Beispiele folgen

user.first_name = u'Marcin'
user.last_name = u'Król'
user.save()
>>> SUCCEED

MySQL-Einstellungen

mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       | 
| character_set_connection | utf8                       | 
| character_set_database   | utf8                       | 
| character_set_filesystem | binary                     | 
| character_set_results    | utf8                       | 
| character_set_server     | utf8                       | 
| character_set_system     | utf8                       | 
| character_sets_dir       | /usr/share/mysql/charsets/ | 
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

Zeichensatz und Sortierreihenfolge

Tabelle auth_user hat utf-8-Zeichensatz mit der Sortierung utf8_general_ci.

Ergebnisse des UPDATE-Befehls

Beim Aktualisieren der obigen Werte in die Tabelle auth_user mithilfe des Befehls UPDATE wurde kein Fehler ausgegeben.

mysql> update auth_user set last_name='Slatkevičiusa' where id=1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select last_name from auth_user where id=100;
+---------------+
| last_name     |
+---------------+
| Slatkevi?iusa | 
+---------------+
1 row in set (0.00 sec)

PostgreSQL

Die oben aufgeführten fehlgeschlagenen Werte können in eine PostgreSQL-Tabelle aktualisiert werden, wenn ich das Datenbank-Backend in Django umstelle. Es ist komisch.

mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset  | Description                 | Default collation   | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8     | UTF-8 Unicode               | utf8_general_ci     |      3 | 
...

Aber aus http://www.postgresql.org/docs/8.1/interactive/multibyte.html habe ich Folgendes gefunden:

Name Bytes/Char
UTF8 1-4

Bedeutet es, dass Unicode-Zeichen in PostgreSQL maximal 4 Byte, aber in MySQL 3 Byte haben, die den obigen Fehler verursacht haben?

141
jack

Ich habe nur eine Methode gefunden, um Fehler zu vermeiden.

In Datenbank speichern

user.first_name = u'Rytis'.encode('unicode_escape')
user.last_name = u'Slatkevičius'.encode('unicode_escape')
user.save()
>>> SUCCEED

print user.last_name
>>> Slatkevi\u010dius
print user.last_name.decode('unicode_escape')
>>> Slatkevičius

Ist dies die einzige Methode, um solche Zeichenfolgen in einer MySQL-Tabelle zu speichern und zu dekodieren, bevor sie für die Anzeige in Templates gerendert wird?

8
jack

Keine dieser Antworten löste das Problem für mich. Die Hauptursache ist:

Sie können in MySQL keine 4-Byte-Zeichen mit dem Zeichensatz utf-8 speichern.

MySQL hat ein -Byte-Limit für utf-8-Zeichen (ja, es ist verrückt, gut zusammengefasst von einem Django Entwickler hier )

Um dies zu lösen, müssen Sie:

  1. Ändern Sie Ihre MySQL-Datenbank, -Tabelle und -Spalten so, dass sie den tf8mb4-Zeichensatz (nur verfügbar ab MySQL 5.5) verwenden.
  2. Geben Sie den Zeichensatz in Ihrer Django Einstellungsdatei wie folgt an:

settings.py

DATABASES = {
    'default': {
        'ENGINE':'Django.db.backends.mysql',
        ...
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}

Hinweis: Beim erneuten Erstellen Ihrer Datenbank kann das Problem " Angegebener Schlüssel war zu lang " auftreten.

Die wahrscheinlichste Ursache ist ein CharFieldmit einer maximalen Länge von 255 und einer Art Index (z. B. eindeutig). Da utf8mb4 33% mehr Speicherplatz benötigt als utf-8, müssen Sie diese Felder um 33% verkleinern.

Ändern Sie in diesem Fall die max_length von 255 auf 191.

Alternativ können Sie bearbeiten Sie Ihre MySQL-Konfiguration, um diese Einschränkung zu entfernen, aber nicht ohne Django Hackery

UPDATE: Ich bin gerade auf dieses Problem gestoßen und landete Wechsel zu PostgreSQL , weil ich meinen VARCHARnicht auf 191 reduzieren konnte Zeichen.

111
donturner

Ich hatte das gleiche Problem und löste es, indem ich den Zeichensatz der Spalte änderte. Obwohl Ihre Datenbank einen Standardzeichensatz von utf-8 hat, denke ich, dass Datenbankspalten möglicherweise einen anderen Zeichensatz in MySQL haben. Hier ist die SQL-Abfrage, die ich verwendet habe:

    ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
    CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
111
user27478

Wenn Sie dieses Problem haben, haben Sie hier ein Python-Skript, um alle Spalten Ihrer mysql-Datenbank automatisch zu ändern.

#! /usr/bin/env python
import MySQLdb

Host = "localhost"
passwd = "passwd"
user = "youruser"
dbname = "yourdbname"

db = MySQLdb.connect(Host=host, user=user, passwd=passwd, db=dbname)
cursor = db.cursor()

cursor.execute("ALTER DATABASE `%s` CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'" % dbname)

sql = "SELECT DISTINCT(table_name) FROM information_schema.columns WHERE table_schema = '%s'" % dbname
cursor.execute(sql)

results = cursor.fetchall()
for row in results:
  sql = "ALTER TABLE `%s` convert to character set DEFAULT COLLATE DEFAULT" % (row[0])
  cursor.execute(sql)
db.close()
65
madprops

Wenn es sich um ein neues Projekt handelt, würde ich einfach die Datenbank löschen und eine neue mit einem richtigen Zeichensatz erstellen:

CREATE DATABASE <dbname> CHARACTER SET utf8;
20
Vanuan

Sie können die Kollatierung Ihres Textfelds in UTF8_general_ci ändern und das Problem wird gelöst.

Beachten Sie, dass dies in Django nicht möglich ist.

6
Wei An

Sie versuchen nicht, Unicode-Zeichenfolgen zu speichern, sondern Bytestrings in der UTF-8-Codierung. Machen Sie sie zu tatsächlichen Unicode-String-Literalen:

user.last_name = u'Slatkevičius'

oder (wenn Sie keine String-Literale haben), decodieren Sie sie mit der utf-8-Codierung:

user.last_name = lastname.decode('utf-8')
1
Thomas Wouters

Verändern Sie einfach Ihren Tisch, Sie brauchen nichts zu tun. Führen Sie einfach diese Abfrage in der Datenbank aus. ALTER TABLE table_nameCONVERT TO CHARACTER SET utf8

es wird definitiv funktionieren.

0
Rishabh Jhalani