Algo que aprendi de mi primer trabajo como programador, en cuanto al tema de base de datos, es siempre obtener, única y exclusivamente, todo lo que necesitemos y nada más.
Es común que cuando comenzamos en el mundo de base de datos nuestras consultas sean un SELECT *, denotando que deseamos obtener todas las columnas involucradas en la consulta. Si bien el SELECT * funciona bastante bien para tablas con pocas columnas o un número pequeño de datos, también hay que mencionar que, si siempre obtenemos todas las columnas de nuestro data set, tarde o temprano, más temprano que tarde, tendremos problemas de performances. Ya que para el gestor de base de datos será mucho más complicado obtener docenas de columnas a un par de ellas.
Por lo tanto la invitación siempre será obtener los datos que necesitemos, ni una dato más ni un dato menos.
Con esto en mente, el día de hoy me gustaría hablemos de cómo podemos obtener datos en utilizando el ORM de Django, definiendo, explícitamente los valores que queremos obtener.
Si eres una persona nueva en el lenguaje este post te resultará de sumo interese, así que te invito a que te quedes.
Bien, sin más introducción, comencemos de una vez.
Método Values
Para este post nos apoyaremos del siguiente modelo.
class Author(models.Model):
name = models.CharField(max_length=100)
biography = models.TextField()
email = models.EmailField(unique=True)
date_of_birth = models.DateField(null=True, blank=True)
website = models.URLField(max_length=200, null=True, blank=True)
affiliation = models.CharField(max_length=100, null=True, blank=True)
def __str__(self):
return self.name
Un modelo bastante sencillo, pero con un par de atributos (columnas) que nos serán de utilidad.
A partir de este modelo, comencemos con una consulta sencilla, obtengamos el id y el nombre de todos los autores que posean una página web.
Por default Django retornará todos los atributos de un objeto al realizarse una consulta, para evitar esto, y al ser este nuestro primer ejemplo, haremos uso del método values, método que nos permite definir que atributos deseamos obtener.
Nuestra consulta puede quedar así:
>>> authors = (Author.objects.filter(website__isnull=False)
.values('id', 'name')
)
>>> authors
<QuerySet [{'id': 1, 'name': 'J.K. Rowling'}, ... }]>
Si echamos un vistazo a nuestro objeto authors nos daremos cuenta que será un objeto QuerySet de diccionarios. Cada diccionario representa a un author y cada diccionario poseerá por llaves las columnas/atributos que hemos definido.
Es muy importante tener en cuenta que, al hacer uso del método values trabajaremos con diccionario, y no con objetos de nuestro modelo.
Por lo tanto, si queremos imprimir en consola el id y el nombre de los autores, el código puede quedar de la siguiente manera.
for author in authors:
print(author['id'], author['name'] )
Haremos uso de llaves y no de atributos.
Este método resulta de utilidad cuando sabemos exactamente los atributos que queremos obtener de la consulta.
Método values_list
Otra forma de obtener atributos de nuestros modelos es usando el método values_list. Este método, a diferencia de values, nos retornará un QuerySet de tuplas.
Veamos un ejemplo.
>>> authors = ( Author.objects.filter(website__isnull=False)
.values_list('id', 'name')
)
>>> authors
<QuerySet [(1, 'J.K. Rowling'), (2, 'Stephen King')]>
Si solo queremos obtener un solo atributo, por ejemplo un listado de ids, podemos hacer uso del parámetro Flat.
>>> authors = ( Author.objects.filter(website__isnull=False)
.values_list('id', flat=True)
)
>>> authors
<QuerySet [1, 2]>
El método values_list , en conjunto con el parámetro flat, resulta particularmente útil cuando deseamos obtener un listado de valores que usaremos después, por ejemplo, un listado de ids que usaremos en una sub consulta o en alguna otra operación.
Método Only
Bien, ya sabemos que con Django podemos obtener un QuerySet de diccionarios o tuplas, pero, ¿Qué pasa si lo que deseamos es trabajar con objetos de nuestro modelo? Bueno, en esos caso haremos uso del método only.
Siguiendo con nuestro ejemplo, usando el método only nuestra consulta puede quedar de la siguiente manera.
>>> authors = Author.objects.only('id', 'name')
>>> authors
<QuerySet [<Author: J.K. Rowling>, <Author: Stephen King>]>
>>> authors.first().id
1
>>> authors.first().name
'J.K. Rowling'
Algo que debemos tener en cuenta al usar el método only es que, si intentamos acceder a un atributo que no fue definido dicho método, Django no se lanzará ningún tipo de error, si no que hará una nueva consulta para obtenerlo. A estos atributos los podemos conocer lazy, estos atributos solo obtendrán su valor cuando sean requeridos. Otra forma de mejorar el performance de nuestra aplicación.
>>> uthors.first().website # Defer attribute
'www.pywombat.com'
Closing notes
Cómo pudiste observar, en Django es muy sencillo el poder definir con que columnas deseamos trabajar. Esto nos permite mejorar el performance de nuestras consultas, haciendo que estas sean mucho más eficientes, lo cual se traduce en páginas mucho más rápidas.
Así que ya lo sabes, siempre que desees realizar una consulta a tu base de datos ten en cuenta que solo debes obtener los datos que necesites, y para esto en Django puedes hacer uso de los métodos values, values_list y only.