viernes, 11 de junio de 2010

Evitando accesos innecesarios a la Base de Datos mientras usamos la API de QuerySet de Django

Recientemente tuve que realizar algunas mejoras de desempeño a un sitio web hecho en django, y me encontre un mal uso de la API del QuerySet, que me gustaria compartir con ustedes

Es muy comun que dados 2 tablas, digase Categoria y Subcategoria, donde Subcategoria tiene como llave foranea a Categoria. Necesitemos listar las Subcategorias, y para cada subcategoria mostrar a que categoria pertenece.

[any_view.py]

def foobar_view(request):
data_template = {
'subcategorias' : Subcategoria.objects.all()
}
return render_to_response('template.html', data_template)

[template.html]

<table>
<tr>
<td>Subcategoria</td>
<td>Categoria</td>
</tr>
{% for subcategoria in subcategorias %}
<tr>
<td>{{ subcategoria.nombre }}</td>
<td>{{ subcategoria.categoria.nombre }}</td>
</tr>
{% endfor %}
</table>


Ahora donde esta el error aqui.

Primero dejemos bien claro dos cosas:

1-los QuerySet tiene un comportamiento "retardado", es decir, que el acceso a la base de datos no ocurre hasta que no se accesa al objeto como tal

2-Cuando nos referimos a un campo ForeignKey, estamos haciendo un "acceso" a la base para obtener el elemento al que hace referencia este campo a menos que lo hayamos cargado con anterioridad

Abiendo aclarado estos 2 puntos, en el ejemplo anterior, estamos accediendo a la BD para obtener las subcategorias, y luego por cada iteracion, estamos accediendo a la BD para obtener la categoria a la que pertenece la subcategoria.

Como quedaría resuelto este gravísimo problema:

haciendo uso del metodo "select_related" de la API de QuerySet

[any_view.py]

def foobar_view(request):
data_template = {
'subcategorias' : Subcategoria.objects.select_related()
}
return render_to_response('template.html', data_template)


3 Maneras de invocar select_realted

1-selected_related()
Profundiza solo en los campos de tipo ForeignKey que no puedan ser nulos
Tiene un comportamiento recursivo, si en la relacion, a su vez tiene relacion con otro modelo

2-selected_related(depth = 2)
Profundiza solo en los campos de tipo ForeignKey que no puedan ser nulos
Avanza tanto niveles se especifique en el parametro

3-selected_related(field_name_list)
Profundiza en los campos especificados, independientemente que puedan ser nulos

Reglas a la hora de usar select_related
-No admite combinar el uso del parametro depth con la lista de campos
-La variante 2 y 3, son incluidas en la API de Query a partir de django 1.0


Espero esto les pueda ser util para mejorar el rendimiento de sus aplicaciones hechas en django. Como siempre, me despido de ustedes hasta la proxima vez que encuentre algo interesante que aportar a la comunidad, por mas sencillo que sea.

Bibliografia consultada
http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4