Sí, lo sé, el título es feo, pero es que el error es feo de verdad!

Estaba revisando el panel de control de TopMetin.net, una web que hice en Django cuando de pronto, al ir a revisar los comentarios ZASCA error 500. Insisto, y el mensaje hace otro tanto. Hmmm, qué raro, es como si hubiese algún comentario jodiendo el listado de comentarios… vale, vamos a revisar los errores, allí encontraré al causante:

File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 433, in __str__
return force_text(self).encode('utf-8')

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2: ordinal not in range(128)

El error salta en un archivo interno de django. Preocupante, estoy acostumbrado a que los fallos vengan del código que yo escribo… Bueno, no será para tanto, tiene pinta de que alguien ha escrito algo extraño en un comentario pienso para mis adentros. Se me ocurre revisar los comentarios en la página, en vez de en el panel de administrador. Así de paso averiguo en qué servidor se ha escrito el comentario para ir estrechando el rango.
Total, voy servidor por servidor (son pocos, cosa de 1 minuto) y no peta por ningún lado. Hmmm, esto empieza a mosquearme, vuelvo al panel de control, despistado y me da por ir pinchando en todos los modelos:

Users correcto, Comments sigue petando, Payments correcto, Servers correcto, Tags correcto, Uptimes correcto, Votes peta. WTF tú también?

No cunda el pánico, tengo una idea, son momentos de crisis y recurro al arma más potente de todo Ingeniero Informático: buscar el error en Google!!
Salen muchas cosas, bien, StackOverflow copa los primeros resultados, ahahá esto está hecho. O no… que si UTF-8, que si Unicode… si vale, es evidente que se trata de un caracter «extraño» que ASCII no traga, eso ya lo había deducido, pero por qué peta en el admin de django y no en la página??? No consigo dar con el problema, Google me ha fallado. Está bien, vayamos al origen del problema, algún dato en la base de datos es especial, tan especial que nunca había petado hasta ahora, por tanto examinemos la base de datos: entro a la BD y voy directo a la tabla de comentarios. Ordeno los resultados para ver los más recientes primero y doy un vistazo rápido… nada. Consulto en Google a qué caracteres puede corresponder el 0xc3 por afinar la vista y veo que se trata de alguna vocal acentuada o una eñe, qué típico, vuelvo a la búsqueda pero no doy con ello. Curiosamente ningún comentario lleva tildes en las primeras 3 letras (el error dice posición 2, por tanto debe ser la tercera letra…). En la tabla de comentarios no hay nada más que eso, comentarios, fechas y números de usuarios y servers. Decido ver la tabla de votos, ya que también peta al listarlos… pero ahí hay menos, solo fechas, IPs e IDs. Y entonces surge la pregunta del millón: ¿qué tienen en común comentarios y votos? Si petan los dos, no será por cosas distintas, de hecho el error es idéntico. Y la respuesta es inmediata: el usuario. Vale, el usuario vota y comenta, pero si es el usuario ¿por qué no peta al listar usuarios? ¿y por qué en el admin y no en la web?

Aparto la vista de la pantalla y junto todos los datos: un usuario, probablemente su nombre, contiene caracteres acentuados o eñes y se está intentando mostrar como ASCII al listar comentarios y votos en el panel de administrador. Ok, vayamos al código, por ejemplo el del Vote que es más sencillo:

class Vote(models.Model):
        server          = models.ForeignKey(Server)
        user            = models.ForeignKey(User)
        date            = models.DateTimeField(auto_now=True, auto_now_add=True)
        ip              = models.CharField(max_length=200, blank=True)

        def __unicode__(self):
                return "%s voted %s" % (self.user,self.server)

¿No hay muchas Strings ahí verdad? De hecho solo hay una, que es precisamente la String que se usa para representar el voto en el panel de administración. Una String que concatena el nombre del usuario que vota y el nombre del servidor. Sí, ahí está el problema, es una String sin ninguna codificación en concreto, por tanto usa ASCII por defecto. FAIL!!

Solución

¿Cómo solucionarlo? Sencillo, tan fácil como añadir una letra, sólo una:

return u"%s voted %s" % (self.user,self.server)

¿Se aprecia la «u» delante de la String? Pues así se le dice a Python que la String es Unicode. Basta poner eso ahí y en la clase Comments (y ya de paso en todas partes jeje) para solucionar el dichoso problema. Reiniciamos apache y a correr.

If you think my content is worth it you can Buy me a Coffee at ko-fi.com Buy me a Ko-fi