Otrzymałem dziwny komunikat o błędzie, gdy próbowałem zapisać first_name, last_name w 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
Udane przykłady
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)
Zestaw znaków i sortowanie w tabeli
Tabela auth_user ma zestaw znaków utf-8 z sortowaniem utf8_general_ci.
Wyniki polecenia UPDATE
Nie spowodowało to żadnego błędu podczas aktualizacji powyższych wartości do tabeli auth_user 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
Wymienione powyżej wartości, które nie powiodły się, można zaktualizować do tabeli PostgreSQL po przełączeniu bazy danych w Django. To dziwne.
mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
...
Ale z http://www.postgresql.org/docs/8.1/interactive/multibyte.html znalazłem:
Name Bytes/Char
UTF8 1-4
Czy oznacza to, że znak Unicode ma maksymalnie 4 bajty w PostgreSQL, ale 3 bajty w MySQL, co spowodowało powyższy błąd?
Odpowiedzi:
Żadna z tych odpowiedzi nie rozwiązała problemu za mnie. Podstawową 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 znaki utf-8 (tak, jest głupi, ładnie podsumowany przez programistę Django tutaj )
Aby rozwiązać ten problem, musisz:
settings.py
Uwaga: podczas ponownego tworzenia bazy danych możesz napotkać problem „ Określony klucz był za długi ”.
Najbardziej prawdopodobną przyczyną jest
CharField
długość maksymalna równa 255 i jakiś indeks (np. Unique). Ponieważ utf8mb4 zużywa o 33% więcej miejsca niż utf-8, musisz zmniejszyć te pola o 33%.W takim przypadku zmień max_length z 255 na 191.
Alternatywnie możesz edytować konfigurację MySQL, aby usunąć to ograniczenie, ale nie bez hakera django
AKTUALIZACJA: Ponownie napotkałem ten problem i skończyłem przełączając się na PostgreSQL, ponieważ nie byłem w stanie zredukować moich
VARCHAR
znaków do 191.źródło
'charset': 'utf8mb4'
opcja w ustawieniach Django jest krytyczna, jak powiedział @Xerion. Wreszcie problem z indeksem to bałagan. Usuń indeks na kolumnie lub ustaw jego długość nie większą niż 191 lub użyjTextField
zamiast tego!Miałem ten sam problem i rozwiązałem go, zmieniając zestaw znaków w kolumnie. Mimo że baza danych ma domyślny zestaw znaków
utf-8
, myślę, że kolumny bazy danych mogą mieć inny zestaw znaków w MySQL. Oto zapytanie SQL, którego użyłem:źródło
Jeśli masz ten problem, oto skrypt w Pythonie, który automatycznie zmienia wszystkie kolumny bazy danych mysql.
źródło
db.commit()
wcześniejdb.close()
.Jeśli jest to nowy projekt, po prostu upuściłbym bazę danych i utworzył nową z odpowiednim zestawem znaków:
źródło
- --character-set-server=utf8
Właśnie wymyśliłem jedną metodę, aby uniknąć powyższych błędów.
Zapisz do bazy danych
Czy jest to jedyna metoda zapisywania takich łańcuchów w tabeli MySQL i dekodowania ich przed renderowaniem do szablonów w celu wyświetlenia?
źródło
.encode('unicode_escape')
rzeczywistości nie przechowujesz znaków Unicode w bazie danych. Zmuszasz wszystkich klientów do odkodowania przed ich użyciem, co oznacza, że nie będzie to działać poprawnie z django.admin lub wieloma innymi rzeczami.utf8
zestawie znaków MySQL 5.1 .utf8mb4
które pozwala na przechowywanie czegoś więcej niż tylko Basic Multilingual Plane. Wiem, można by pomyśleć, że „UTF8” to wszystko, co jest potrzebne do pełnego przechowywania Unicode. Cóż, co wiesz, tak nie jest. Zobacz dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.htmlMożesz zmienić sortowanie swojego pola tekstowego na UTF8_general_ci, a problem zostanie rozwiązany.
Zauważ, że nie można tego zrobić w Django.
źródło
Nie próbujesz zapisywać ciągów znaków Unicode, tylko próbujesz zapisać bajtesty w kodowaniu UTF-8. Ustaw je jako rzeczywiste literały znaków Unicode:
lub (jeśli nie masz literałów łańcuchowych) dekoduj je za pomocą kodowania utf-8:
źródło
Po prostu zmień swój stół, nie musisz nic robić. po prostu uruchom to zapytanie w bazie danych. ALTER TABLE
table_name
CONVERT TO CHARACTER SET utf8to na pewno zadziała.
źródło
Ulepszenie @madprops answer - rozwiązanie jako polecenie zarządzania django:
Mam nadzieję, że to pomoże nikomu oprócz mnie :)
źródło