La creación y actualización de datos seguro es divertida, pero también es inútil sin una forma de tamizar los datos. Ya hemos visto una forma de examinar todos los datos de un determinado modelo:
>>> Publisher.objects.all()
[<Publisher: Addison-Wesley>, <Publisher: O'Reilly>, <Publisher: Apress Publishing>]
Eso se traslada a esto en SQL:
SELECT
id, name, address, city, state_province, country, website
FROM book_publisher;
Nota Nota que Django no usa SELECT *
cuando busca datos y en cambio lista
todos los campos explícitamente. Esto es una decisión de diseño: en
determinadas circunstancias SELECT *
puede ser lento, y (más importante)
listar los campos sigue el principio del Zen de Python: "Explícito es mejor
que implícito".
Para más sobre el Zen de Python, intenta escribiendo import this
en el
prompt de Python.
Echemos un vistazo a cada parte de esta linea Publisher.objects.all()
:
- En primer lugar, tenemos nuestro modelo definido,
Publisher
. Aquí no hay nada extraño: cuando quieras buscar datos, usa el modelo para esto. - Luego, tenemos
objects
. Técnicamente, esto es un administrador (manager). Los administradores son discutidos en el Apéndice B. Por ahora, todo lo que necesitas saber es que los administradores se encargan de todas las operaciones a "nivel de tablas" sobre los datos incluidos, y lo más importante, las consultas.
Todos los modelos automáticamente obtienen un administrador `objects`; debes usar el mismo cada vez que quieras consultar sobre una instancia del modelo.
- Finalmente, tenemos
all()
. Este es un método del administradorobjects
que retorna todas las filas de la base de datos. Aunque este objeto se parece a una lista, es realmente un QuerySet — un objeto que representa algún conjunto de filas de la base de datos. El Apéndice C describe QuerySets en detalle. Para el resto de este capítulo, sólo trataremos estos como listas emuladas.
Cualquier búsqueda en base de datos va a seguir esta pauta general — llamaremos métodos del administrador adjunto al modelo en el cual queremos hacer nuestra consulta.
5.11.1. Filtrar datos
Aunque obtener todos los objetos es algo que ciertamente tiene su utilidad, la
mayoría de las veces lo que vamos a necesitar es manejarnos sólo con un
subconjunto de los datos. Para ello usaremos el método filter()
:
>>> Publisher.objects.filter(name="Apress Publishing")
[<Publisher: Apress Publishing>]
filter()
toma argumentos de palabra clave que son traducidos en las
cláusulas SQL WHERE
apropiadas. El ejemplo anterior sería traducido en algo
como:
SELECT
id, name, address, city, state_province, country, website
FROM book_publisher
WHERE name = 'Apress Publishing';
Puedes pasarle a filter()
múltiples argumentos para reducir las cosas aún
más:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress Publishing>]
Esos múltiples argumentos son traducidos a cláusulas SQL AND
. Por lo tanto
el ejemplo en el fragmento de código se traduce a lo siguiente:
SELECT
id, name, address, city, state_province, country, website
FROM book_publisher
WHERE country = 'U.S.A.' AND state_province = 'CA';
Notar que por omisión la búsqueda usa el operador SQL =
para realizar
búsquedas exactas. Existen también otros tipos de búsquedas:
>>> Publisher.objects.filter(name__contains="press")
[<Publisher: Apress Publishing>]
Notar el doble guión bajo entre name
y contains
. Del mismo modo que
Python, Django usa el doble guión bajo para indicar que algo "mágico" está
sucediendo — aquí la parte __contains
es traducida por Django en una
sentencia SQL LIKE
:
SELECT
id, name, address, city, state_province, country, website
FROM book_publisher
WHERE name LIKE '%press%';
Hay disponibles varios otos tipos de búsqueda, incluyendo icontains
(LIKE
no sensible a diferencias de mayúsculas/minúsculas), startswith
y endswith
,
y range
(consultas SQL BETWEEN
). El Apéndice C describe en detalle todos
esos tipos de búsqueda.
5.11.2. Obtener objetos individuales
En ocasiones desearás obtener un único objeto. Para esto existe el método
get()
:
>>> Publisher.objects.get(name="Apress Publishing")
<Publisher: Apress Publishing>
En lugar de una lista (o más bien, un QuerySet), este método retorna un objeto individual. Debido a eso, una consulta cuyo resultado sean múltiples objetos causará una excepción:
>>> Publisher.objects.get(country="U.S.A.")
Traceback (most recent call last):
...
AssertionError: get() returned more than one Publisher -- it returned 2!
Una consulta que no retorne objeto alguno también causará una excepción:
>>> Publisher.objects.get(name="Penguin")
Traceback (most recent call last):
...
DoesNotExist: Publisher matching query does not exist.
5.11.3. Ordenar datos
A medida que juegas con los ejemplos anteriores, podrías descubrir que los objetos son devueltos en lo que parece ser un orden aleatorio. No estás imaginándote cosas, hasta ahora no le hemos indicado a la base de datos cómo ordenar sus resultados, de manera que simplemente estamos recibiendo datos con algún orden arbitrario seleccionado por la base de datos.
Eso es, obviamente, un poco ingenuo. No quisiéramos que una página Web que
muestra una lista de editores estuviera ordenada aleatoriamente. Así que, en la
práctica, probablemente querremos usar order_by()
para reordenar nuestros
datos en listas más útiles:
>>> Publisher.objects.order_by("name")
[<Publisher: Addison-Wesley>, <Publisher: Apress Publishing>, <Publisher: O'Reilly>]
Esto no se ve muy diferente del ejemplo de all()
anterior, pero el SQL
incluye ahora un ordenamiento específico:
SELECT
id, name, address, city, state_province, country, website
FROM book_publisher
ORDER BY name;
Podemos ordenar por cualquier campo que deseemos:
>>> Publisher.objects.order_by("address")
[<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
>>> Publisher.objects.order_by("state_province")
[<Publisher: Apress Publishing>, <Publisher: Addison-Wesley>, <Publisher: O'Reilly>]
y por múltiples campos:
>>> Publisher.objects.order_by("state_provice", "address")
[<Publisher: Apress Publishing>, <Publisher: O'Reilly>, <Publisher: Addison-Wesley>]
También podemos especificar un ordenamiento inverso antecediendo al nombre del
campo un prefijo -
(el símbolo menos):
>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
Aunque esta flexibilidad es útil, usar order_by()
todo el tiempo puede ser
demasiado repetitivo. La mayor parte del tiempo querrás ordenar por un
determinado campo. Es esos casos Django te permite anexar al modelo un
ordenamiento por omisión para el mismo:
class Publisher(models.Model):
name = models.CharField(maxlength=30)
address = models.CharField(maxlength=50)
city = models.CharField(maxlength=60)
state_province = models.CharField(maxlength=30)
country = models.CharField(maxlength=50)
website = models.URLField()
def __str__(self):
return self.name
class Meta:
ordering = ["name"]
Este fragmento ordering = ["name"]
le indica a Django que a menos que se
proporcione un ordenamiento mediante order_by()
, todos los editores deberán
ser ordenados por su nombre.
Nota Django usa esta class Meta
interna como un lugar en el cual se pueden
especificar metadatos adicionales acerca de un modelo. Es completamente
opcional, pero puede realizar algunas cosas muy útiles. Examina el Apéndice
B para conocer las opciones que puede poner bajo Meta
.
5.11.4. Encadenar búsquedas
Has visto cómo puedes filtrar datos y has visto cómo ordenarlos. En ocasiones, por supuesto, vas a desear realizar ambas cosas. En esos casos simplemente "encadenas" las búsquedas entre sí:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
Como podrías esperar, esto se traduce a una consulta SQL conteniendo tanto un
WHERE
como un ORDER BY
:
SELECT
id, name, address, city, state_province, .. code-block:: sqlcountry, website
FROM book_publisher
WHERE country = 'U.S.A.'
ORDER BY name DESC;
Puedes encadenar consultas en forma consecutiva tantas veces como desees. No existe un límite para esto.