Как предсказать ошибку при сохранении символов utf8 в таблице latin-1 mysql через django?
Я использую python3+, django 3.2 и mysql 5.7 на AWS Amazon linux instance. Когда я первоначально создавал свою базу данных и таблицы, я не указал конкретную кодировку. Поэтому я прочитал следующее сообщение и определил, что мои таблицы и столбцы в настоящее время имеют кодировку latin1: Как посмотреть, какой набор символов у базы данных/таблицы/столбца MySQL?
Я также прочитал это сообщение, чтобы попытаться понять разницу между тем, что клиент использует в качестве кодировки, и тем, что использует таблица/база данных - это позволяет клиенту сохранять символы, отличные от латиницы, в таблице mysql с кодировкой latin1: MySQL 'set names latin1', похоже, заставляет данные храниться как utf8
Вот некоторый код, чтобы показать, что я пытаюсь сделать:
# make a new object
mydata = Dataset()
# set the description. This has a few different non-latin1 characters:
# smart quotes, long dash, dots over the i
mydata.description = "“naïve—T-cells”"
# this returns an error to prove to myself that there are non-latin1 chars in the string
mydata.description.encode("latin-1")
# Traceback (most recent call last):
# File "<console>", line 1, in <module>
# UnicodeEncodeError: 'latin-1' codec cant encode character '\u201c' in position 0:
# ordinal not in range(256)
# this works though (ie this string can be encoded using cp1252)
mydata.description.encode("cp1252")
# >>> b'\x93na\xefve\x97T-cells\x94'
# And, it is fine to save it to the mysql table (which has latin1 charset, but I
# believe this works since the client can handle non-latin1 as I read from above link)
# no error for this:
mydata.save()
# now I try again but with a different non-latin1 character (greater than or equal sign)
mydata.description = "≥4"
# both of these give an error as expected, since the >= character isnt in either charset
mydata.description.encode("latin-1")
mydata.description.encode("cp1252")
# I cant save this non-latin1 char to the database:
mydata.save()
# django.db.utils.OperationalError: (1366, "Incorrect string value: '\\xE2\\x89\\xA54' for column 'description' at row 1")
Мой вопрос: почему некоторые символы нелатинского алфавита сохраняются без проблем, но другие символы нелатинского алфавита вызывают ошибку "OperationalError Incorrect string value", когда я пытаюсь их вставить?
Возможно, я мог бы решить проблему, изменив charset в таблицах mysql (Django charset and encoding), но у меня есть приложение, развернутое у нескольких разных клиентов, поэтому это довольно сложно (мягко сказано). Вместо этого я хотел бы создать шаг в процессе загрузки данных, который проверяет наличие недопустимых символов, а не выдает ошибку, чтобы пользователь мог внести изменения в документ до загрузки.
Итак, мой практический вопрос: как узнать, какие нелатинские символы вызовут проблему, а какие можно? Можно ли сохранять все символы cp1252, но все, что выходит за пределы cp1252, не разрешено?
Как я могу проверить, какую кодировку использует мой клиент django? (У меня нет ничего связанного с charset или именами наборов в моих DATABASE Options в settings.py)
Примечание: Я не хочу, чтобы что-то изменяло таблицы или требовало миграции. Я хочу предотвратить ошибки, информируя пользователей о плохих символах.
Перейдите к инструменту командной строки "mysql". Используйте его для выполнения команды SHOW CREATE TABLE tablename; Это сообщит вам наборы символов (и кодировки) для столбцов этой таблицы.
SET NAMES latin1; объявляет, что клиент имеет кодировку latin1, а не cp1252, не UTF-8 и т.д.
\x93na\xefve\x97T-cells\x94 - это cp1256 или latin1 для “naïve—T-cells”. Следовательно, SET должен был помочь.
latin1 hex: 936E61EF766597542D63656C6C7394
utf8 hex: E2809C6E61C3AF7665E28094542D63656C6C73E2809D
'double-encoded': C3A2E282ACC5936E61C383C2AF7665C3A2E282ACE2809D542D63656C6C73C3A2E282ACC29D
(Мой ответ в ссылке относился к "двойному кодированию" в пункте 7)
E289A5 is utf8 for `≥`, which _cannot_ be properly encoded in latin1.
Итак, если вы видите ≥ в клиенте, то это не latin1, и некоторые вещи в вашем вопросе требуют дальнейшего изучения. Вот кодировки, в которых это будет работать.
binary, utf8mb4, utf8 E289A5
euckr A1C3
gb18030, gb2312, gbk A1DD
keybcs2 F2
koi8r, koi8u 99
macce, macroman B3
Суть в том, что вы должны использовать UTF-8 (MySQL's "utf8mb4") для всего.