2010-01-30 8 views
8

Acabo de crear el siguiente modelo:Django dolor de cabeza con simple cadena no ASCII

class Categoria(models.Model): 
    nombre=models.CharField(max_length=30) 
    padre=models.ForeignKey('self', blank=True, null=True) 

    def __unicode__(self): 
     return self.nombre 

Entonces certificado a la interfaz de administración y syncdb'd

Todo bien si sólo añadir caracteres ASCII plano. Pero si añado un "Categoria" nombre "a" (por decir algo) me sale:

Environment: 

Request Method: GET 
Request URL: http://192.168.2.103:8000/administracion/locales/categoria/ 
Django Version: 1.1.1 
Python Version: 2.6.4 
Installed Applications: 
['django.contrib.auth', 
'django.contrib.contenttypes', 
'django.contrib.sessions', 
'django.contrib.admin', 
'cruzandoelsuquiaDJ.locales'] 
Installed Middleware: 
('django.middleware.common.CommonMiddleware', 
'django.contrib.sessions.middleware.SessionMiddleware', 
'django.contrib.auth.middleware.AuthenticationMiddleware') 


Template error: 
In template /usr/lib/pymodules/python2.6/django/contrib/admin/templates/admin/change_list.html, error at line 78 
    Caught an exception while rendering: ('ascii', '\xc3\xa1', 0, 1, 'ordinal not in range(128)') 
    68 :   {% endif %} 


    69 :  {% endblock %} 


    70 :  


    71 :  <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}> 


    72 :  {% if cl.formset %} 


    73 :   {{ cl.formset.management_form }} 


    74 :  {% endif %} 


    75 : 


    76 :  {% block result_list %} 


    77 :   {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %} 


    78 :   {% result_list cl %} 


    79 :   {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %} 


    80 :  {% endblock %} 


    81 :  {% block pagination %}{% pagination cl %}{% endblock %} 


    82 :  </form> 


    83 :  </div> 


    84 : </div> 


    85 : {% endblock %} 


    86 : 

Traceback: 
File "/usr/lib/pymodules/python2.6/django/core/handlers/base.py" in get_response 
    92.     response = callback(request, *callback_args, **callback_kwargs) 
File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in wrapper 
    226.     return self.admin_site.admin_view(view)(*args, **kwargs) 
File "/usr/lib/pymodules/python2.6/django/views/decorators/cache.py" in _wrapped_view_func 
    44.   response = view_func(request, *args, **kwargs) 
File "/usr/lib/pymodules/python2.6/django/contrib/admin/sites.py" in inner 
    186.    return view(request, *args, **kwargs) 
File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in changelist_view 
    986.   ], context, context_instance=context_instance) 
File "/usr/lib/pymodules/python2.6/django/shortcuts/__init__.py" in render_to_response 
    20.  return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs) 
File "/usr/lib/pymodules/python2.6/django/template/loader.py" in render_to_string 
    108.  return t.render(context_instance) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    178.   return self.nodelist.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    779.     bits.append(self.render_node(node, context)) 
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node 
    71.    result = node.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render 
    97.   return compiled_parent.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    178.   return self.nodelist.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    779.     bits.append(self.render_node(node, context)) 
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node 
    71.    result = node.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render 
    97.   return compiled_parent.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    178.   return self.nodelist.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    779.     bits.append(self.render_node(node, context)) 
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node 
    71.    result = node.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render 
    24.   result = self.nodelist.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    779.     bits.append(self.render_node(node, context)) 
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node 
    71.    result = node.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render 
    24.   result = self.nodelist.render(context) 
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render 
    779.     bits.append(self.render_node(node, context)) 
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node 
    81.    raise wrapped 

Exception Type: TemplateSyntaxError at /administracion/locales/categoria/ 
Exception Value: Caught an exception while rendering: ('ascii', '\xc3\xa1', 0, 1, 'ordinal not in range(128)') 

Mi versión Django es 1.1 y mi base de datos es 5.1.37-1ubuntu5 con juego de caracteres UTF-8 y la mesa utiliza una colación utf8_bin.

Este problema parece demasiado básico para ser verdad, y yo soy un novato Django así que lo siento por adelantado si me falta algo muy simple :)

+0

Me parece que el mismo Django debería resolver estos problemas por sí mismo. Probablemente deberías probar las más recientes combinaciones de python Django y MySQL, asegúrate de tener todas las cadenas en Unicode (y los encabezados Unicode en tus fuentes de Python - '# - * - codificación: utf-8 - * -'). Si nada de esto ayuda, consideraría archivarlo como un error en Django. – che

+0

Uno pensaría que ... Trataré de archivar el error. – Ezequiel

Respuesta

17

Django generalmente tiene muy buena compatibilidad con Unicode (ver Django 1.1 "Unicode data" documentation para más detalles). En mi código encuentro que, si tengo un problema con las funciones simples de Unicode, el problema generalmente es que no estoy entendiendo bien los detalles de Django, no es que Django tenga un error en su compatibilidad con Unicode.

La página "Datos Unicode" nos dice que "Todos los backends de la base de datos de Django ... automáticamente convierten cadenas extraídas de la base de datos en cadenas Unicode de Python. Ni siquiera necesita decirle a Django qué codificación utiliza su base de datos: se maneja de forma transparente ". Por lo tanto, su simple return self.nombre debe devolver una cadena de Python Unicode.

Sin embargo, el Django 1.1 "Databases" page tiene una nota importante acerca de cómo el motor MySQL se encarga del cotejo utf8_bin:

... si realmente quiere mayúsculas y minúsculas comparaciones sobre una columna en particular o mesa, lo haría cambie la columna o la tabla para usar la intercalación utf8_bin. Lo principal a tener en cuenta en este caso es que si está utilizando MySQLdb 1.2.2, el servidor de base de datos de Django luego volverá cadenas de bytes (en lugar de cadenas Unicode) para cualquier carácter campos Devuelve reciben del base de datos. Esta es una fuerte variación de la práctica normal de Django de que siempre devuelve cadenas de Unicode. Es depende de usted, el desarrollador, manejar el hecho de que recibirá cadenas de bytes si configura su tabla para usar la intercalación utf8_bin. Django en sí debería funcionar sin problemas con tales columnas, pero si su código debe estar preparado para solicitar django.utils.encoding.smart_unicode() a veces si lo que realmente quiere trabajar con datos consistentes ...

Por lo tanto, en su ejemplo original, la columna "nombre" utilizó la intercalación utf8_bin. Esto significaba que self.nombre devolvía una cadena de bytes de Python.Cuando lo pones en una expresión que requiere una cadena de Python Unicode, Python realizó su conversión predeterminada. Esto es el equivalente a self.nombre.decode('ascii'). Y por supuesto, .decode('ascii') falla cuando encuentra cualquier byte por encima de 0x7F, como los bytes UTF-8 que codifican "á".

Descubrió las dos formas de resolver este problema. La primera es convertir la cadena de bytes de Python devuelta por self.nombre en una cadena de Unicode de Python explícitamente. Apuesto el siguiente código más simple hubiera funcionado:

return self.nombre.decode('utf8') 

El segundo enfoque es cambiar la intercalación de MySQL para la columna "nombre", lo que provoca motor MySQL de Django para devolver cadenas Unicode de Python en lugar de las cadenas de bytes inusuales . Luego, su expresión original da una cadena Unicode de Python:

return self.nombre 

Espero que esto ayude.

+0

Sí, me ayuda a comprender. Marqué su respuesta como correcta porque está muy bien explicada. Gracias – Ezequiel

+1

¿Cómo se cambia el tipo de clasificación para una columna MySQL? – MikeN

+0

@MikeN Esto podría ser útil para usted http://stackoverflow.com/questions/6050014/how-do-you-change-the-collation-type-for-a-mysql-column –

2

Ok ...

return u"%s"%(self.nombre.decode('utf8'),) 

hace el truco.

Pero también descubrió que el cambio de utf8_bin a utf8_general_ci hace el truco, es decir, self.nombre funciona como se esperaba.

6

Este problema se puede resolver modificando un poco el código de django. Añadir un código a continuación en django/utils/encoding.py

import sys 
reload(sys) 
sys.setdefaultencoding('utf-8') 
4

tuve este problema en la producción y no en el servidor de desarrollo.
Luego me di cuenta de que se crearon nuevas tablas con la intercalación utf8_bin en lugar de utf8_general_ci.

Para ver las tablas que requiere conversión, el tipo

SHOW TABLE STATUS; 

luego convertir aquellos con utf8_bin cotejo escribiendo

ALTER TABLE app_table CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; 

Por último, cambiar la colación por defecto para que esto no vuelva a suceder:

ALTER DATABASE my_database character set utf8 collate utf8_general_ci; 
0

Tuve un problema similar cuando cambié recientemente una tabla MySQL para usar la intercalación utf8_bin en la etapa mientras que no hay problema en dev (python2.7, Django1.4.2 en ambos entornos). Descubrí que en el desarrollo tengo MySQL-python 1.2.4c1 y en la puesta en escena tengo 1.2.3. La actualización a MySQL-python 1.2.4 resolvió el problema para mí.

1

Lo resuelvo por problema simplemente creando un Modelo de Administración para el Modelo e incluyendo todas las variables en "list_display".

+0

que debería ser un comentario – VladL

+0

sry, soy nuevo aquí –

Cuestiones relacionadas