python - def - encoding django
Error de "error de cadena incorrecta" de MySQL al guardar una cadena Unicode en Django (7)
Acabo de descubrir un método para evitar los errores anteriores.
Guardar en la base de datos
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
¿Es este el único método para guardar cadenas de ese tipo en una tabla MySQL y decodificarlas antes de representarlas en plantillas para su visualización?
Recibí un extraño mensaje de error cuando intenté guardar first_name, last_name en el modelo auth_user de Django.
Ejemplos fallidos
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
Ejemplos de éxito
user.first_name = u''Marcin''
user.last_name = u''Król''
user.save()
>>> SUCCEED
Configuración de 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)
Juego de caracteres de tabla y colación
La tabla auth_user tiene un conjunto de caracteres utf-8 con intercalación utf8_general_ci.
Resultados del comando ACTUALIZAR
No generó ningún error al actualizar los valores anteriores a la tabla auth_user utilizando el comando 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
Los valores erróneos enumerados anteriormente se pueden actualizar en la tabla PostgreSQL cuando cambié el backend de la base de datos en Django. Es extraño.
mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
...
Pero desde http://www.postgresql.org/docs/8.1/interactive/multibyte.html , encontré lo siguiente:
Name Bytes/Char
UTF8 1-4
¿Significa que unicode char tiene un máximo de 4 bytes en PostgreSQL pero 3 bytes en MySQL que causó el error anterior?
Ninguna de estas respuestas resolvió el problema para mí. La causa principal es:
No puede almacenar caracteres de 4 bytes en MySQL con el juego de caracteres utf-8.
MySQL tiene un límite de 3 bytes en caracteres utf-8 (sí, es wack, muy bien resumido por un desarrollador de Django aquí )
Para resolver esto, necesitas:
- Cambia tu base de datos MySQL, tabla y columnas para usar el juego de caracteres utf8mb4 (solo disponible desde MySQL 5.5 en adelante)
- Especifique el juego de caracteres en su archivo de configuración de Django de la siguiente manera:
settings.py
DATABASES = {
''default'': {
''ENGINE'':''django.db.backends.mysql'',
...
''OPTIONS'': {''charset'': ''utf8mb4''},
}
}
Nota: Al volver a crear su base de datos, puede encontrarse con el problema ''La clave especificada era demasiado larga ''.
La causa más probable es un CharField
que tiene un max_length de 255 y algún tipo de índice (por ejemplo, único). Debido a que utf8mb4 usa un 33% más de espacio que utf-8 necesitarás hacer que estos campos sean un 33% más pequeños.
En este caso, cambie el max_length de 255 a 191.
Alternativamente, puede editar su configuración de MySQL para eliminar esta restricción, pero no sin hacker de django.
ACTUALIZACIÓN: Acabo de toparme con este problema otra vez y terminé cambiando a PostgreSQL porque no pude reducir mi VARCHAR
a 191 caracteres.
No está intentando guardar cadenas de caracteres unicode, está tratando de guardar cadenas de bytes en la codificación UTF-8. Conviértelos en literales de cadenas unicode reales:
user.last_name = u''Slatkevičius''
o (cuando no tiene literales de cadena) decodifíquelos usando la codificación utf-8:
user.last_name = lastname.decode(''utf-8'')
Puede cambiar la intercalación de su campo de texto a UTF8_general_ci y el problema se resolverá.
Aviso, esto no se puede hacer en Django.
Si se trata de un proyecto nuevo, simplemente soltaría la base de datos y crearía una nueva con un juego de caracteres adecuado:
CREATE DATABASE <dbname> CHARACTER SET utf8;
Si tiene este problema, aquí hay una secuencia de comandos python para cambiar automáticamente todas las columnas de su base de datos 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()
Tuve el mismo problema y lo resolví cambiando el conjunto de caracteres de la columna. Aunque su base de datos tiene un juego de caracteres predeterminado de utf-8
, creo que es posible que las columnas de la base de datos tengan un juego de caracteres diferente en MySQL. Aquí está el SQL QUERY que utilicé:
ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;