Cómo decapar los unicodes y guardarlos en las bases de datos utf-8

Tengo una base de datos (mysql) donde quiero almacenar datos encurtidos.

Los datos pueden ser, por ejemplo, un diccionario, que puede contener unicode, por ejemplo,

data = {1 : u'é'} 

y la base de datos (mysql) está en utf-8.

Cuando me encurtido

 import pickle pickled_data = pickle.dumps(data) print type(pickled_data) # returns  

El pickled_data resultante es una cadena.

Cuando bash almacenar esto en una base de datos (por ejemplo, en un campo de texto), esto puede causar problemas. En particular, estoy llegando en algún momento un

 UnicodeDecodeError "'utf8' codec can't decode byte 0xe9 in position X" 

al intentar guardar el pickled_data en la base de datos. Esto tiene sentido porque pickled_data puede tener caracteres que no sean utf-8. Mi pregunta es ¿cómo almaceno pickled_data en una base de datos utf-8?

Veo dos posibles candidatos:

  1. Codifique el resultado del pickle.dump a utf-8 y guárdelo. Cuando quiero pickle.load, tengo que decodificarlo.

  2. Almacene la cadena encurtida en formato binario (¿cómo?), Lo que obliga a todos los caracteres a estar dentro de ascii.

Mi problema es que no veo cuáles son las consecuencias de elegir una de estas opciones a largo plazo. Dado que el cambio ya requiere un poco de esfuerzo, me siento obligado a pedir una opinión sobre este tema, pidiendo eventualmente mejores candidatos.

(PS Esto es por ejemplo útil en Django )

Los datos de pickle son opacos, datos binarios, incluso cuando utiliza la versión de protocolo 0:

 >>> pickle.dumps(data, 0) '(dp0\nI1\nV\xe9\np1\ns.' 

Cuando intenta almacenar eso en un TextField , Django intentará decodificar esos datos a UTF8 para almacenarlos; esto es lo que falla porque no se trata de datos codificados en UTF-8; son datos binarios en su lugar:

 >>> pickled_data.decode('utf8') Traceback (most recent call last): File "", line 1, in  File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: 'utf8' codec can't decode byte 0xe9 in position 9: invalid continuation byte 

La solución es no intentar almacenar esto en un TextField . Utilice un campo BinaryField en BinaryField lugar:

Un campo para almacenar datos binarios en bruto. Sólo soporta la asignación de bytes . Tenga en cuenta que este campo tiene una funcionalidad limitada. Por ejemplo, no es posible filtrar un conjunto de consultas en un valor de BinaryField.

Tienes un valor de bytes (las cadenas de Python 2 son cadenas de bytes, cuyo nombre se cambia a bytes en Python 3).

Si insiste en almacenar los datos en un campo de texto, descodifíquelos explícitamente como latin1 ; El codec Latin 1 asigna bytes uno-a-uno a puntos de código Unicode:

 >>> pickled_data.decode('latin1') u'(dp0\nI1\nV\xe9\np1\ns.' 

y asegúrese de codificarlo de nuevo antes de volver a seleccionar:

 >>> encoded = pickled_data.decode('latin1') >>> pickle.loads(encoded) Traceback (most recent call last): File "", line 1, in  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/pickle.py", line 1381, in loads file = StringIO(str) UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 9: ordinal not in range(128) >>> pickle.loads(encoded.encode('latin1')) {1: u'\xe9'} 

Tenga en cuenta que si deja que este valor vaya al navegador y vuelva a aparecer en un campo de texto, es probable que el navegador haya reemplazado los caracteres en esos datos. Internet Explorer reemplazará los \n caracteres por \r\n , por ejemplo, porque asume que se trata de texto.

No es que alguna vez deba permitir aceptar datos de salmuera de una conexión de red en cualquier caso, porque es un agujero de seguridad en espera de explotación .