J'ai reçu un message d'erreur étrange lorsque j'ai essayé de sauvegarder prénom_nom, nom_dernier sur le modèle auth_user de Django.
Exemples échoués
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
Exemples réussis
user.first_name = u'Marcin'
user.last_name = u'Król'
user.save()
>>> SUCCEED
Paramètres MySQL
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)
Jeu de caractères et classement
La table auth_user a un jeu de caractères utf-8 avec un classement utf8_general_ci.
Résultats de la commande UPDATE
Il n'a généré aucune erreur lors de la mise à jour des valeurs précédentes dans la table auth_user à l'aide de la commande UPDATE.
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
Les valeurs d'échec répertoriées ci-dessus peuvent être mises à jour dans la table PostgreSQL lorsque j'ai basculé le backend de la base de données dans Django. C'est étrange.
mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
...
Mais de http://www.postgresql.org/docs/8.1/interactive/multibyte.html , j'ai trouvé ce qui suit:
Name Bytes/Char
UTF8 1-4
Est-ce que cela signifie que le caractère unicode a un maximum de 4 octets dans PostgreSQL mais de 3 octets dans MySQL, ce qui a causé l'erreur ci-dessus?
Je viens de trouver une méthode pour éviter les erreurs ci-dessus.
Enregistrer dans la base de données
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
Est-ce la seule méthode pour enregistrer de telles chaînes dans une table MySQL et les décoder avant de les restituer aux modèles à afficher?
J'ai eu le même problème et je l'ai résolu en modifiant le jeu de caractères de la colonne. Même si votre base de données a un jeu de caractères par défaut de utf-8
, il est possible que les colonnes de base de données aient un jeu de caractères différent dans MySQL. Voici la requête SQL que j'ai utilisée:
ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
Aucune de ces réponses n'a résolu le problème pour moi. La cause fondamentale étant:
Vous ne pouvez pas stocker des caractères de 4 octets dans MySQL avec le jeu de caractères utf-8.
MySQL a une limite de 3 octets sur les caractères utf-8 (oui, c'est wack, bien résumé par un développeur de Django ici )
Pour résoudre ce problème, vous devez:
settings.py
DATABASES = {
'default': {
'ENGINE':'Django.db.backends.mysql',
...
'OPTIONS': {'charset': 'utf8mb4'},
}
}
Remarque: Lorsque vous recréez votre base de données, vous pouvez rencontrer le problème ' La clé spécifiée était trop long '.
La cause la plus probable est une CharField
qui a une longueur maximale de 255 et une sorte d’index dessus (par exemple, unique). Comme utf8mb4 utilise 33% plus d’espace que utf-8, vous aurez besoin de réduire ces champs de 33%.
Dans ce cas, modifiez la longueur maximale de 255 à 191.
Sinon, vous pouvez éditer votre configuration MySQL pour supprimer cette restrictionmais pas sans Django hackery
UPDATE: Je viens juste de rencontrer à nouveau ce problème et je me suis retrouvé en passant à PostgreSQL car je ne pouvais pas réduire ma VARCHAR
à 191 caractères.
Si vous avez ce problème, voici un script python pour changer automatiquement toutes les colonnes de votre base de données mysql.
#! /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()
S'il s'agit d'un nouveau projet, il suffit de supprimer la base de données et d'en créer un nouveau avec un jeu de caractères approprié:
CREATE DATABASE <dbname> CHARACTER SET utf8;
Vous pouvez modifier le classement de votre champ de texte en UTF8_general_ci et le problème sera résolu.
Notez que cela ne peut pas être fait dans Django.
Vous n'essayez pas de sauvegarder des chaînes unicode, vous essayez de sauvegarder des chaînes de caractères dans le codage UTF-8. Faites-en de vrais littéraux de chaînes unicode:
user.last_name = u'Slatkevičius'
ou (lorsque vous n'avez pas de littéraux de chaîne), décodez-les en utilisant le codage utf-8:
user.last_name = lastname.decode('utf-8')
Modifiez simplement votre table, pas besoin de rien. Il suffit d’exécuter cette requête sur la base de données .ALTER TABLE table_name
CONVERT TO CHARACTER SET utf8
ça va définitivement marcher.