Błąd MySQL "incorrect string value" podczas zapisywania ciągu unicode w Django

Dostałem dziwny komunikat o błędzie, gdy próbowałem zapisać first_name, last_name do modelu Auth_user Django.

Nieudane przykłady

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

Przykłady sukcesu

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

Ustawienia 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)

Table charset and collation

Tabela auth_user ma kod utf-8 z kolacją utf8_general_ci.

Wyniki polecenia UPDATE

Nie wywołało żadnego błędu podczas aktualizacji powyższych wartości do auth_user tabelę za pomocą polecenia 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

Nieudane wartości wymienione powyżej mogą zostać zaktualizowane do tabeli PostgreSQL, gdy zmieniłem backend bazy danych w Django. To dziwne.

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

Ale od http://www.postgresql.org/docs/8.1/interactive/multibyte.html , znalazłem:

Name Bytes/Char
UTF8 1-4

Czy to oznacza, że znak unicode ma maxlen 4 bajtów w PostgreSQL, ale 3 bajty w MySQL, co spowodowało powyższy błąd?

Author: Alan Moore, 2010-01-21

8 answers

Miałem ten sam problem i rozwiązałem go zmieniając zestaw znaków w kolumnie. Mimo że Twoja baza danych ma domyślny zestaw znaków utf-8, myślę, że możliwe jest, aby kolumny bazy danych miały inny zestaw znaków w MySQL. Oto zapytanie SQL, którego użyłem:

    ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
    CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
 108
Author: user27478,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-04 14:59:08

Żadna z tych odpowiedzi nie rozwiązała problemu. Główną przyczyną jest:

Nie można przechowywać 4-bajtowych znaków w MySQL z zestawem znaków utf-8.

MySQL ma limit 3 bajtów na utf-8 znaków (tak, to wariactwo, ładnie podsumowane przez dewelopera Django tutaj)

Aby to rozwiązać, musisz:

  1. ZMIEŃ bazę danych, tabelę i kolumny MySQL, aby używać zestawu znaków utf8mb4 (dostępne tylko z MySQL 5.5 dalej)
  2. Określ zestaw znaków w Twoim pliku ustawień Django jak poniżej:

Settings.py

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

Uwaga: Podczas odtwarzania bazy danych możesz napotkać problem " określony klucz był zbyt długi".

Najbardziej prawdopodobną przyczyną jest CharField, która ma max_length 255 i jakiś indeks (np. unique). Ponieważ utf8mb4 zużywa o 33% więcej miejsca niż utf-8, będziesz musiał zmniejszyć te pola o 33%.

W tym przypadku zmień max_length od 255 do 191.

Alternatywnie możesz edytować konfigurację MySQL, aby usunąć to ograniczenie ale nie bez django hackery

UPDATE: po prostu natknąłem się na ten problem ponownie i skończyło się przejście na PostgreSQL ponieważ nie byłem w stanie zmniejszyć mój VARCHAR do 191 znaków.

 98
Author: donturner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-30 21:07:56

Jeśli masz ten problem Oto skrypt Pythona, aby automatycznie zmienić wszystkie kolumny bazy danych 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()
 64
Author: madprops,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-05 22:35:28

Jeśli jest to nowy projekt, po prostu rzuciłbym bazę danych i utworzył nowy z odpowiednim charsetem:

CREATE DATABASE <dbname> CHARACTER SET utf8;
 17
Author: Vanuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-21 19:57:05

Właśnie wymyśliłem jedną metodę, aby uniknąć powyższych błędów.

Zapisz do bazy danych

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

Czy jest to jedyna metoda zapisywania takich ciągów w tabeli MySQL i dekodowania ich przed renderowaniem do szablonów do wyświetlenia?

 6
Author: jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 15:29:09

Możesz zmienić zestawienie swojego pola tekstowego na UTF8_general_ci i problem zostanie rozwiązany.

Uwaga, tego nie można zrobić w Django.

 6
Author: Wei An,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-02 15:54:06

Nie próbujesz zapisać ciągów unicode, próbujesz zapisać bajty w kodowaniu UTF-8. Uczyń z nich rzeczywiste literały ciągu unicode:

user.last_name = u'Slatkevičius'

Lub (jeśli nie masz liter ciągów) dekodować je za pomocą kodowania utf-8:

user.last_name = lastname.decode('utf-8')
 1
Author: Thomas Wouters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 11:44:50

Po prostu zmień swój stół, nie ma potrzeby niczego. po prostu uruchom to zapytanie w bazie danych. ALTER TABLE table_name CONVERT TO CHARACTER SET utf8

Na pewno zadziała.
 0
Author: Rishabh Jhalani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-13 12:41:40