Django filtro de query usando un string

Es común que queramos pasar directamente un string al filtro de una consulta, por ejemplo, desde una url. Dado que el parámetro de filter es un diccionario, lo podemos hacer mediante el operador de desempaquetado de diccionarios **.

Recordemos que para desempaquetar una lista usamos el operador * y para desempaquetar un diccionario el **:

Ejemplo de *


>>> pepe=(1,10,)
>>> range(pepe)
Traceback (most recent call last):
File "", line 1, in
TypeError: range() integer end argument expected, got tuple.
>>> range(*pepe)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Ejemplo de **



>>> pepe={ "venus" : "blanco", "tierra": "azul", "marte": "rojo" }
>>> '{tierra}'.format(pepe)
Traceback (most recent call last):
File "", line 1, in
KeyError: 'tierra'
>>> '{tierra}'.format(**pepe)
'azul'
>>>


En ambos ejemplos observamos que al usar * o ** pasamos con el tipo correcto. Pero ¿Cómo aprovecharlo para pasar directamente un filtro a la consulta?. Yo lo hago de la siguiente forma:


# un string
filtro="nacionalidad__exact@ESP"
# lo parseamos en clave-valor
f=filtro.split("@")
# pasamos como diccionario
p=Persona.objects.filter(**{f[0]:f[1]})
# el efecto es el mismo que hacer
p=Persona.objects.filter(nacionalidad__exact='ESP')

Y ahora veamos cómo implementarlo.

En la página web:

$('a.pais').click(function() {
   var pais=$(this).attr("pais");
   $('#{{entidad}}').load("?filtro="+escape('nacionalidad__exact@'+pais)+"&type=ajax");
});


Notemos que en la url pasamos,
  1. Un interrogante: Hará un get sobre la página actual.
  2. Un nombre de parámetro (filtro). Si queremos añadir más parámetros los separamos con &
  3. El valor del parametro poniendo un igual después del nombre, por ejemplo,  filtro=nacionalidad__exact@ESP 
En la parte del servidor lo que hacemos es procesar directamente el filtro pasando de string a diccionario así:


def get_filtro( self, request):
  """ aplica el filtro y devuelve el modelo filtrado """
   filtro=request.GET.get('filtro', False)
   if filtro:
  try:
     f=filtro.split('@')
     return Persona.objects.filter(**{f[0]:f[1]})
   except Exception, e:
    logger.error('desempaquetando filtro '+str(e))

   return Persona.objects.all()


La ventaja de este método es que es muy sencillo crear toda clase de filtros directamente en la página web teniendo un solo código que los gestiona en el servidor. 

Una última puntualización: Si no todos los usuarios pueden ver todo el modelo i.e. ver todas las personas Persona.objects.all() hay que ir con cuidado ya que es muy sencillo manipular a mano la url para que nos devuelva cualquier registro. Por ejemplo:

?filtro=moroso__exact@1

Nos devolvería todos los morosos ;-)



Reproducir vídeos en la Nexus 10 por USB

De forma nativa no podemos conectar un disco externo o pen drive a la Nexus para reproducir vídeo. Para poderlo hacer precisamos dos cosas:

  1. Que la Nexus nos haga de "host" USB para que pueda leer desde dispositivos USB.
  2. Eventualmente, poder reproducir el formato AVI.

Conexión del dispositivo USB a la Nexus 10

Necesitamos un cable OTG (USB On-the-go) para poder conectar el dispositivo (hembra) en el micro-usb de la tablet. Son muy baratos y se encuentran en cualquier tienda de componentes o electrónica.






También necesitamos que el sistema operativo nos reconozca el dispositivo. Como android no lo hace, tenemos que instalar software. Una aplicación que funciona sin problemas (no es necesario entrar como root) es Nexus Media Importer. Basta con conectar el dispositivo y ya tendremos acceso al contenido del disco.



Reproducción de AVI

Hay muchos buenos programas que reproducen AVI y mantienen los códecs actualizados. Yo uso el MX Player Pro, al que podemos activar la decodificación por hardware (en la pestaña de Configuración-Decodificación)


Django error "name field too short" generando permisos en el admin si verbose_name está presente

El verbose_name de un modelo sirve para dar una descripción extendida de él. Si está presente, es usada por el admin para describir el modelo, si no, se usa el nombre de la clase. Por ejemplo:

class Cliente(models.Model):
  ...
  ...
   verbose_name=u"Esta descripción saldrá en el admin, es un poco larga para mostrar el error"
   verbose_name_plural=u"Esta es la descripción que se usa en plural"


Para poder administrar el modelo, lo incluimos y registramos en admin.py:

class ClienteAdmin(admin.ModelAdmin):  
    list_display=('nombre','codigo',)
    ....
admin.site.register(Cliente, ClienteAdmin)  


En el primer runserver que hagamos, el admin intentará registrar el modelo y crear los permisos de add, change y delete. El problema es que la descripción del permiso es del estilo de "Can add "+verbose_name y se almacena en el campo name de la tabla auth_permission que está definido con 80 caracteres de largo. Por lo tanto, la creación de permisos fallará si se supera esa longuitud y nos encontraremos con el modelo registrado en el admin pero sin poderle asignar permisos.

Registré el bug y resulta que lo han cerrado como duplicado  de otro de hace cinco años!!!. Claro que es fácil quejarse y no currar en el proyecto....

De momento, para resolverlo basta con editar a mano la longitud del campo:

ALTER TABLE auth_permission MODIFY name VARCHAR(500);


Consola linux en utf8 (debian, ubuntu)

Un problema común cuando usamos la cónsola para pasar datos de una aplicación a otra o para pasar un dump de mysql a otro servidor es que la cónsola no acepta utf8 y nos corrompe los caracteres extendidos.

Para que podamos usar la cónsola en utf8 con es_ES, hemos de asegurarnos, en primer lugar, de que los locales están generados haciendo:

sudo locale-gen es_ES.UTF-8
sudo dpkg-reconfigure locales


y para que la sesión de bash use dicha configuración añadir en el .bashrc

export LANG=es_ES.UTF-8
export LANGUAGE=es_ES.UTF-8


Si no funciona y estamos iniciando la sesión desde otra máquina hemos de asegurarnos de que el cliente también tenga dicha configuración. Si estamos usando putty para una sesión ssh hay que ir a Preferences-Window-Translation y poner el remote character set en UTF-8 y guardar el perfil.