Datos Unicode

Django admite datos Unicode en cualquier lugar.

Este documento te dice qué debes saber si estás escribiendo aplicaciones que utilizan datos o plantillas codificadas en algo distinto de ASCII.

Creando la base de datos

Asegúrate de que tu base de datos esté configurada para poder almacenar datos de cadena arbitrarios. Normalmente, esto significa darle un encoding de UTF-8 o UTF-16. Si utilizas una codificación más restrictiva – por ejemplo, latin1 (iso8859-1) – no podrás almacenar ciertos caracteres en la base de datos y se perderá información.

  • Los usuarios de MySQL, consulta el manual de MySQL para obtener detalles sobre cómo establecer o alterar la codificación del conjunto de caracteres de la base de datos.

  • Los usuarios de PostgreSQL, consulta el manual de PostgreSQL para obtener detalles sobre cómo crear bases de datos con la codificación correcta.

  • Los usuarios de Oracle, consulta el manual de Oracle para obtener detalles sobre cómo establecer (sección 2) o alterar (sección 11) la codificación del conjunto de caracteres de la base de datos.

  • Los usuarios de SQLite no necesitan hacer nada. SQLite siempre utiliza UTF-8 para la codificación interna.

Todos los backends de bases de datos de Django convierten automáticamente las cadenas en la codificación adecuada para comunicarse con la base de datos y también convierten automáticamente las cadenas recuperadas de la base de datos en cadenas. No necesitas decirle a Django qué codificación utiliza tu base de datos: eso se maneja de manera transparente.

Para más información, consulta la sección «La API de bases de datos» a continuación.

General manejo de cadenas

Cuando utilices cadenas con Django – por ejemplo, en consultas de base de datos, renderizado de plantillas o en cualquier otro lugar – tienes dos opciones para codificar esas cadenas. Puedes utilizar cadenas normales o bytestrings (que comienzan con un “b”).

Advertencia

Una bytestring no lleva información sobre su codificación. Por eso, tenemos que hacer una suposición y Django asume que todas las bytestrings están en UTF-8.

Si pasas una cadena a Django que ha sido codificada en algún otro formato, las cosas funcionarán mal de maneras interesantes. Normalmente, Django levantará un UnicodeDecodeError en algún punto.

Si tu código solo utiliza datos ASCII, es seguro utilizar tus cadenas normales, pasándolas a voluntad porque ASCII es un subconjunto de UTF-8.

No te engañes pensando que si el parámetro DEFAULT_CHARSET está configurado en algo distinto de 'utf-8' puedes utilizar ese otro codificador en tus bytestrings. El parámetro DEFAULT_CHARSET solo se aplica a las cadenas generadas como resultado del renderizado de plantillas (y correos electrónicos). Django siempre asumirá la codificación UTF-8 para las bytestrings internas. La razón es que el parámetro DEFAULT_CHARSET no está realmente bajo tu control (si eres desarrollador de aplicaciones). Está bajo el control de la persona que instala y utiliza tu aplicación – y si esa persona elige un valor diferente, tu código debe seguir funcionando. Por lo tanto, no puede confiar en ese parámetro.

En la mayoría de los casos cuando Django está tratando con cadenas, las convertirá a cadenas antes de hacer cualquier otra cosa. Así que como regla general, si pasas una bytestring, prepárate para recibir una cadena en el resultado.

Cadenas traducidas

Además de cadenas y bytestrings, hay un tercer tipo de objeto similar a una cadena que podrías encontrar al utilizar Django. Las características de internacionalización del marco introducen el concepto de «traducción perezosa» – una cadena que ha sido marcada como traducida pero cuyo resultado de traducción real no se determina hasta que el objeto se utiliza en una cadena. Esta característica es útil en casos donde la ubicación de traducción es desconocida hasta que la cadena se utilice, aunque la cadena podría haberse creado originalmente cuando el código se importó.

Normalmente, no tendrás que preocuparte por las traducciones perezosas. Solo ten en cuenta que si examinas un objeto y afirma ser un django.utils.functional.__proxy__ objeto, es una traducción perezosa. Llamar a str() con la traducción perezosa como argumento generará una cadena en el locale actual.

Para obtener más detalles sobre objetos de traducción relajados, consulta la documentación de internacionalización en <doc>`internacionalización </topics/i18n/index>`.

Funciones útiles

Porque algunas operaciones con cadenas se repiten una y otra vez, Django incluye un par de funciones útiles que deberían hacer que trabajar con objetos de cadena y bytestring sea un poco más fácil.

Funciones de conversión

El módulo django.utils.encoding contiene algunas funciones que son útiles para convertir entre cadenas y bytestrings.

  • smart_str(s, encoding='utf-8', strings_only=False, errors='strict') convierte su entrada a una cadena. El parámetro encoding especifica el código de encabezado de entrada. (Por ejemplo, Django utiliza esto internamente cuando procesa datos de formulario de entrada, que pueden no estar codificados en UTF-8.) El parámetro strings_only, si se establece en True, dará como resultado que los números de Python, booleanos y None no sean convertidos a una cadena (mantienen sus tipos originales). El parámetro errors toma cualquier de los valores aceptados por la función str() de Python para su manejo de errores.

  • force_str(s, encoding='utf-8', strings_only=False, errors='strict') es idéntico a smart_str() en casi todos los casos. La diferencia es cuando el primer argumento es una instancia de traducción relajada . Mientras que smart_str() preserva las traducciones relajadas, force_str() fuerza esos objetos a una cadena (causando la traducción a ocurrir). Normalmente, quieres usar smart_str(). Sin embargo, force_str() es útil en etiquetas de plantilla y filtros que absolutamente deben tener una cadena para funcionar con ella, no solo algo que se puede convertir a una cadena.

  • smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict') esencialmente es lo opuesto a smart_str(). Fuerza el primer argumento a un bytestring. El parámetro strings_only tiene el mismo comportamiento que para smart_str() y force_str(). Esto tiene semánticas ligeramente diferentes de la función builtin str() de Python, pero la diferencia es necesaria en unos pocos lugares dentro de los internos de Django.

Normalmente, solo necesitarás usar force_str(). Llámalo lo antes posible en cualquier dato de entrada que podría ser tanto una cadena como un bytestring, y desde entonces puedes tratar el resultado como siempre siendo una cadena.

Manejo de URI e IRI

Los textos traducidos manteniendo todas las etiquetas intactas son:

Estos dos grupos de funciones tienen fines ligeramente diferentes, y es importante mantenerlos separados. Normalmente, utilizarías quote() en las partes individuales del camino IRI o URI para que cualquier carácter reservado como “&” o “%” estén correctamente codificados. Luego, aplicas iri_to_uri() al IRI completo y convierte cualquier carácter no ASCII a los valores codificados correctos.

Nota

Técnicamente, no es correcto decir que iri_to_uri() implementa el algoritmo completo en la especificación de IRI. No lo hace (todavía) realizar la codificación del nombre internacional de dominio parte del algoritmo.

La función iri_to_uri() no cambiará los caracteres ASCII que de otra manera están permitidos en una URL. Por ejemplo, el carácter “%” no se codificará aún más cuando se pasa a iri_to_uri(). Esto significa que puedes pasar una URL completa a esta función y no la estropeará la cadena de consulta o algo similar.

Un ejemplo podría clarificar las cosas aquí:

>>> from urllib.parse import quote
>>> from django.utils.encoding import iri_to_uri
>>> quote("Paris & Orléans")
'Paris%20%26%20Orl%C3%A9ans'
>>> iri_to_uri("/favorites/François/%s" % quote("Paris & Orléans"))
'/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans'

Si miras con cuidado, puedes ver que la parte generada por quote() en el segundo ejemplo no se doble-citó cuando se pasó a iri_to_uri(). Esto es un aspecto muy importante y útil. Significa que puedes construir tu IRI sin preocuparte de si contiene caracteres no ASCII y luego, justo al final, llama a iri_to_uri() en el resultado.

De manera similar, Django proporciona django.utils.encoding.uri_to_iri() que implementa la conversión de URI a IRI según RFC 3987 Section 3.2.

Un ejemplo para demostrar:

>>> from django.utils.encoding import uri_to_iri
>>> uri_to_iri("/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93")
'/♥♥/?utf8=✓'
>>> uri_to_iri("%A9hello%3Fworld")
'%A9hello%3Fworld'

Los textos traducidos son:

Ambas funciones iri_to_uri() y uri_to_iri() son idempotentes, lo que significa que la siguiente es siempre verdadero:

iri_to_uri(iri_to_uri(some_string)) == iri_to_uri(some_string)
uri_to_iri(uri_to_iri(some_string)) == uri_to_iri(some_string)

Entonces puedes llamarlo con seguridad varias veces en el mismo URI/IRI sin correr riesgo de problemas de doble comillas.

Modelos

Porque todas las cadenas se devuelven desde la base de datos como objetos str, los campos del modelo que son basados en caracteres (CharField, TextField, URLField, etc.) contendrán valores Unicode cuando Django recupera datos de la base de datos. Esto es siempre el caso, incluso si los datos podrían caber en un bytestring ASCII.

Puedes pasar cadenitas cuando estés creando un modelo o poblándolo con un campo, y Django convertirá a cadenas cuando lo necesite.

Tomar cuidado en get_absolute_url()

Las URLs solo pueden contener caracteres ASCII. Si estás construyendo una URL desde piezas de datos que podrían ser no-ASCII, ten cuidado de codificar los resultados de manera adecuada para una URL. La función reverse() maneja esto automáticamente.

Si estás construyendo manualmente una URL (es decir, no usando la función reverse(), tendrás que encargarte tú mismo del codificado. En este caso, usa las funciones iri_to_uri() y quote() documentadas anteriormente. Por ejemplo:

from urllib.parse import quote
from django.utils.encoding import iri_to_uri


def get_absolute_url(self):
    url = "/person/%s/?x=0&y=0" % quote(self.location)
    return iri_to_uri(url)

Esta función devuelve una URL correctamente codificada incluso si self.location es algo como «Jack visitó París & Orléans». (De hecho, la llamada a iri_to_uri() no es estrictamente necesaria en el ejemplo anterior, porque todos los caracteres no-ASCII habrían sido eliminados al citar en la primera línea.)

Plantillas

Usa cadenas cuando estés creando manualmente plantillas:

from django.template import Template

t2 = Template("This is a string template.")

Pero el caso común es leer plantillas desde el sistema de archivos. Si tus archivos de plantilla no están almacenados con un codificación UTF-8, ajusta la configuración TEMPLATES. El backend incorporado django proporciona la opción 'file_charset' para cambiar la codificación utilizada para leer archivos desde el disco.

La configuración DEFAULT_CHARSET controla la codificación de las plantillas renderizadas. Esto se establece en UTF-8 por defecto.

Etiquetas y filtros de plantilla

Un par de consejos para recordar cuando estés escribiendo tus propias etiquetas y filtros de plantilla:

  • Siempre devuelve cadenas desde el método render() de una etiqueta de plantilla y desde llamadas a filtros.

  • Utiliza force_str() en lugar de smart_str() en estos lugares. Las llamadas a renderizar etiquetas y a filtros ocurren mientras la plantilla se está renderizando, por lo que no hay ventaja en retrasar la conversión de objetos de traducción perezosos en cadenas. Es más fácil trabajar únicamente con cadenas en ese punto.

Archivos

Si planeas permitir a los usuarios subir archivos, debes asegurarte de que el entorno utilizado para ejecutar Django esté configurado para funcionar con nombres de archivo no ASCII. Si tu entorno no está configurado correctamente, encontrarás excepciones UnicodeEncodeError cuando se guarden archivos con nombres o contenido que contengan caracteres no ASCII.

El soporte del sistema de archivos para nombres de archivo UTF-8 varía y puede depender del entorno. Verifica tu configuración actual en una consola interactiva de Python ejecutando:

import sys

sys.getfilesystemencoding()

Esto debería mostrar «UTF-8».

La variable de entorno LANG es responsable de establecer el código de caracteres esperado en plataformas Unix. Consulte la documentación de su sistema operativo y servidor de aplicación para obtener la sintaxis y ubicación adecuadas para configurar esta variable. Vea el Cómo usar Django con Apache y mod_wsgi para ejemplos.

En tu entorno de desarrollo, es posible que debas agregar una configuración a tu ~.bashrc análoga a:

export LANG="en_US.UTF-8"

Envío de formulario

El envío de formularios HTML es un área complicada. No hay garantía de que el envío incluya información de codificación, lo que significa que el marco puede tener que adivinar la codificación del datos enviados.

Django adopta una «aproximación perezosa» para descodificar los datos de formulario. Los datos en un objeto HttpRequest solo se descodifican cuando los accedes. De hecho, la mayoría de los datos no se descodifican en absoluto. Solo las estructuras de datos HttpRequest.GET y HttpRequest.POST tienen alguna codificación aplicada a ellas. Esas dos propiedades devolverán sus miembros como datos Unicode. Todas las otras atributos y métodos del objeto HttpRequest devuelven los datos exactamente como se enviaron por el cliente.

Por defecto, la configuración DEFAULT_CHARSET se utiliza como codificación asumida para los datos de formulario. Si necesitas cambiar esto para un formulario en particular, puedes establecer la propiedad encoding en una instancia de HttpRequest. Por ejemplo:

def some_view(request):
    # We know that the data must be encoded as KOI8-R (for some reason).
    request.encoding = "koi8-r"
    ...

Puedes incluso cambiar la codificación después de haber accedido a request.GET o request.POST, y todas las accesos posteriores utilizarán la nueva codificación.

La mayoría de los desarrolladores no necesitarán preocuparse por cambiar la codificación de los formularios, pero esta es una característica útil para aplicaciones que hablan con sistemas legados cuya codificación no puedes controlar.

Django no descodifica los datos de subidas de archivos porque ese dato se trata normalmente como colecciones de bytes en lugar de cadenas. Cualquier descodificación automática alteraría el significado del flujo de bytes.