2010-01-30 10 views
8

Ho appena creato il seguente modello:Django mal di testa con semplice stringa non 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 

quindi registrato l'interfaccia di amministrazione e syncdb'd

Tutto ok se ho solo aggiungere caratteri ASCII. Ma se posso aggiungere una "Categoria" denominati "A" (per dire qualcosa) ottengo:

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)') 

La mia versione Django è 1.1 e il mio database è 5.1.37-1ubuntu5 con utf8 charset e la tavola sta usando un regole di confronto utf8_bin.

Questo problema sembra troppo semplice per essere vero, e io sono un newbie Django quindi mi dispiace in anticipo se mi manca qualcosa di molto semplice :)

+0

Mi sembra che Django dovrebbe risolvere da solo questi problemi di questo tipo. Probabilmente dovresti provare i nuovi collegamenti Python Django e MySQL, assicurati di avere tutte le stringhe in unicode (e le intestazioni unicode nei tuoi sorgenti python - '# - * - coding: utf-8 - * -'). Se nulla di questo aiuta, prenderei in considerazione l'idea di archiviarlo come un bug in Django. – che

+0

Si potrebbe pensare che ... proverò a compilare il bug. – Ezequiel

risposta

17

Django ha generalmente un ottimo supporto Unicode (vedere lo Django 1.1 "Unicode data" documentation per i dettagli). Nel mio codice trovo che, se ho un problema con le semplici funzionalità di Unicode, il problema di solito è che non sto capendo bene i dettagli di Django, non che Django abbia un bug nel suo supporto Unicode.

La pagina "Unicode dati" ci dice che "Tutti i backend di database di Django ... automaticamente convertire le stringhe recuperati dal database in stringhe Unicode Python Non è nemmeno bisogno di dire ciò che Django codifica il database utilizza:. Che è gestito in modo trasparente. " Quindi il tuo semplice return self.nombre dovrebbe restituire una stringa Unicode Python.

Tuttavia, il Django 1.1 "Databases" page ha una nota importante su come il backend MySQL gestisce il utf8_bin collazione:

... se si vuole veramente case-sensitive confronti su una particolare colonna o tavolo, si farebbe modificare la colonna o la tabella per utilizzare le regole di confronto utf8_bin. La cosa principale di essere a conoscenza di in questo caso è che se si sta utilizzando MySQLdb 1.2.2, il database back-end in Django tornerà stringhe di byte (invece di stringhe Unicode) per ogni carattere campi restituisce ricevono dal database. Questa è una forte variazione dalla pratica normale di Django di che restituisce sempre stringhe Unicode. Lo spetta a te, lo sviluppatore, di gestire il fatto che riceverai i byte se si configura la tabella per utilizzare le regole di confronto utf8_bin. Django si dovrebbe funzionare senza problemi con tali colonne, ma se il codice deve essere pronto a chiamare django.utils.encoding.smart_unicode() a volte se vuole veramente lavorare con dati coerenti ...

Quindi, nell'esempio originale, la colonna "nombre" ha utilizzato le regole di confronto utf8_bin. Ciò significava che self.nombre restituiva una stringa di byte Python.Quando lo inserisci in un'espressione che richiede una stringa Python Unicode, Python esegue la sua conversione predefinita. Questo è l'equivalente di self.nombre.decode('ascii'). E naturalmente, .decode('ascii') fallisce quando incontra qualsiasi byte sopra 0x7F, come i byte UTF-8 che codificano "á".

Hai scoperto i due modi per risolvere questo problema. Il primo è di convertire esplicitamente la stringa di byte Python restituita da self.nombre in una stringa Unicode Python. Scommetto che il seguente codice più semplice avrebbe funzionato:

return self.nombre.decode('utf8') 

Il secondo approccio è quello di cambiare le regole di confronto di MySQL per la colonna "nombre", che fa sì che MySQL backend di Django per tornare stringhe Python Unicode al posto delle stringhe di byte insolite . Quindi la tua espressione originale fornisce una stringa Unicode Python:

return self.nombre 

Spero che questo aiuti.

+0

Sì, mi aiuta a capire. Ho contrassegnato la tua risposta come corretta perché è così ben spiegata. Grazie – Ezequiel

+1

Come si modifica il tipo di confronto per una colonna MySQL? – MikeN

+0

@MikeN Questo potrebbe essere utile per te 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'),) 

fa il trucco.

Ma ha anche scoperto che cambiare utf8_bin in utf8_general_ci fa il trucco, cioè self.nombre funziona come previsto.

6

Questo problema può essere risolto modificando leggermente il codice di django. Aggiungere codice qui sotto in Django/utils/encoding.py

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

ho avuto questo problema in produzione e non sul server di sviluppo.
Quindi mi sono reso conto che le nuove tabelle sono state create con le regole di confronto utf8_bin anziché utf8_general_ci.

per vedere quali tavoli richiedono la conversione, tipo

SHOW TABLE STATUS; 

poi convertire quelli con utf8_bin collazione digitando

ALTER TABLE app_table CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; 

Infine, modificare le regole di confronto di default quindi questo non accada di nuovo:

ALTER DATABASE my_database character set utf8 collate utf8_general_ci; 
0

Ho avuto un problema simile quando recentemente cambiato una tabella MySQL per usare la collation utf8_bin nella staging mentre nessun problema in dev (python2.7, Django1.4.2 in entrambi gli ambienti). Ho scoperto che in dev ho MySQL-python 1.2.4c1 e nella staging ho 1.2.3. L'aggiornamento a MySQL-python 1.2.4 ha risolto il problema per me.

1

Ho risolto il problema semplicemente creando un modello di amministrazione per il modello e includendo tutte le variabili in "list_display".

+0

che dovrebbe essere un commento – VladL

+0

sry, sono nuovo qui –

Problemi correlati