Error de MySQL "valor de cadena incorrecto" al guardar cadena unicode en Django
Recibí un mensaje de error extraño 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)
Conjunto de caracteres y cotejo de la tabla
La tabla auth_user tiene un conjunto de caracteres utf-8 con intercalación utf8_general_ci.
Resultados del comando de actualización
No generó ningún error al actualizar los valores anteriores a auth_user tabla mediante 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 fallidos listados arriba se pueden actualizar a 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 maxlen de 4 bytes en PostgreSQL pero 3 bytes en MySQL que causaron el error anterior?
8 answers
Tuve el mismo problema y lo resolví cambiando el conjunto de caracteres de la columna. A pesar de que su base de datos tiene un conjunto de caracteres predeterminado de utf-8
Creo que es posible que las columnas de la base de datos tengan un conjunto de caracteres diferente en MySQL. Aquí está la CONSULTA SQL que usé:
ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-04 14:59:08
Ninguna de estas respuestas resolvió el problema para mí. La causa raíz es:
No puede almacenar caracteres de 4 bytes en MySQL con el conjunto 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:
- Cambie su base de datos MySQL, tabla y columnas para usar el conjunto de caracteres utf8mb4 (solo disponible desde MySQL 5.5 en adelante)
- Especifique el conjunto 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 recrear 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 en él (por ejemplo, único). Debido a que utf8mb4 usa un 33% más de espacio que utf-8, necesitará hacer que estos campos sean un 33% más pequeños.
En este caso, cambie la max_length de 255 a 191.
Alternativamente puede editar su configuración de MySQL para eliminar esta restricción pero no sin algo de hacker django
ACTUALIZACIÓN: Me encontré con este problema de nuevo y terminé cambiando a PostgreSQL porque no pude reducir mi VARCHAR
a 191 caracteres.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-30 21:07:56
Si tiene este problema, aquí hay un script python para cambiar todas las columnas de su base de datos mysql automáticamente.
#! /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()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-05 22:35:28
Si se trata de un proyecto nuevo, simplemente soltaría la base de datos y crearía uno nuevo con un conjunto de caracteres adecuado:
CREATE DATABASE <dbname> CHARACTER SET utf8;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-21 19:57:05
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 como esa en una tabla MySQL y decodificarla antes de renderizarla en plantillas para su visualización?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-21 15:29:09
Puede cambiar la intercalación de su campo de texto a UTF8_general_ci y el problema se resolverá.
Tenga en cuenta que esto no se puede hacer en Django.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-02 15:54:06
No está tratando de guardar cadenas unicode, está tratando de guardar bytestrings en la codificación UTF-8. Convertirlos en literales de cadena unicode reales:
user.last_name = u'Slatkevičius'
O (cuando no tiene literales de cadena) descodifíquelos usando la codificación utf-8:
user.last_name = lastname.decode('utf-8')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-21 11:44:50
Simplemente altere su mesa, sin necesidad de nada. simplemente ejecute esta consulta en la base de datos.
ALTER TABLE table_name
CONVERTIR A CONJUNTO DE CARACTERES utf8
Definitivamente funcionará.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-13 12:41:40