Как предсказать ошибку при сохранении символов 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") для всего.

Вернуться на верх