OpenWebinars

Frameworks

Empezando a hacer queries con Django

Aprende de forma práctica a utilizar el ORM de Django para trabajar con bases de datos y realizar todo tipo de queries sobre ellas de una forma sencilla.

Juan Benito Pacheco Rubio

Juan Benito Pacheco Rubio

Experto Backend

Lectura 8 minutos

Publicado el 10 de marzo de 2023

Compartir

¿Quieres interactuar con tu base de datos de manera sencilla?

Con el ORM de Django puedes realizar todo tipo de queries sin complejidad: recuperar información, filtrar y ordenar resultados, usar agregaciones para resumir los datos y mucho más.

En este contenido, encontrarás una guía práctica para empezar a trabajar con bases de datos en Django.

Consultas en Bases de Datos

Una de las necesidades de todo proyecto web, es el poder guardar información que luego pueda ser consultada fácilmente, para esto existen las bases de datos.

Existen diferentes tipos de bases de datos que podemos utilizar: SQL, key-value, Documental, Grafos, Orientada a objetos, Columna ancha

El tipo de nuestra base de datos, dependerá de que datos queremos guardar, y como queremos acceder a ellos más adelante, en función de si necesitamos calcular métricas, información para una red social, baja latencia de acceso a la información.

Conviértete en un Backend Developer
Domina los lenguajes de programación más demandados. Accede a cursos, talleres y laboratorios para crear proyectos con Java, Python, PHP, Microsoft .NET y más
Comenzar gratis ahora

Object Relational Mapping

Debido a los diferentes tipos de bases de datos que existen, y a la popularización de la programación orientada a objetos (OOP), un patrón muy utilizado es el Object Relational Mapping(ORM) es una técnica de software que nos permite interactuar con nuestra base de datos, como si de un objeto más se tratase. De esta forma podemos crear, actualizar o borrar información fácilmente.

Como hemos dicho, este tipo de sistemas son muy utilizados entre los desarrolladores por las ventajas que incluyen:

  • Cómo la facilidad de desarrollo y rápida curva de aprendizaje
  • La eliminación de la necesidad de conocer la tecnología concreta de nuestra base de datos.
  • Incluyen una serie de buenas prácticas, sobre todo en materias de seguridad, cómo la sanitización de los parámetros de entrada, evitando que nuestras consultas sufran problemas como SQL Injection.

Pero estas ventajas no vienen sin un coste, ya que esta tecnología aunque son muy populares también vienen con unos tradeoffs o inconvenientes:

  • Lentitud o problemas de performance en algunos casos; ya que en muchas ocasiones se lanzan consultas innecesarias, y muy costosas.
  • Pérdida de flexibilidad, ya que los ORMs en muchos casos no pueden replicar a la perfección la versatilidad que ofrecen los lenguajes de consultas de las propias bases de datos, y tienen algunas limitaciones.
  • Los desarrolladores pierden conocimiento de lo que está sucediendo en la base de datos, ya que todo este conocimiento cae del lado del ORM, y eso acaba provocando que el ORM se acabe convirtiendo en una caja negra para el desarrollador.

Es importante, que los desarrolladores tienen sean conscientes de estas limitaciones, ya que, aunque ofrecen numerosas ventajas, siempre viene con el coste de eliminar parte de la flexibilidad que proporciona el lenguaje en claro de las bases de datos, y algunos ORMs pueden venir con problemas de performance, o comportamientos indeseados, por lo que es necesario conocer la tecnología que se está utilizando.

Muchos de los frameworks más utilizados por los desarrolladores ya incorporan ORMs para facilitar el desarrollo de aplicaciones: Spring - Java, Ruby on Rails - Ruby, Laravel - PHP

El ORM de Django

Uno de los ORMs más utilizados en el ecosistema Python, es el ORM de Django, no en vano ya que Django es uno de los Frameworks más utilizados entre los amantes de Python, donde destaca la gran comunidad que posee, así como la gran cantidad de libros y cursos de Django a nuestra disposición.

El ORM de Django se focaliza sobre todo en la simplicidad de uso, y la prácticamente nula curva de aprendizaje necesaria para empezar a utilizarlo, además de ser muy versátil. Este ORM funciona sobre todo con Bases de Datos SQL.

El ORM de Django está compuesto de dos componentes principales: Django Models, QuerySets.

Django Models

Los Django Models son los modelos para representar las tablas de nuestra base de datos SQL, y modelar así la información.

class Person(models.Model):
    first_name = models.CharField(max_length=150)
    last_name = models.CharField(max_length=150)
    age = models.IntegerField()

De esta forma cada instancia de nuestro modelo es representa una fila de la tabla, y cada propiedad de la clase una columna.

Una de las partes más deseadas de Django es su capacidad de actualizar la estructura de base de tu base de datos de forma automática, eso lo consigue gracias a los Modelos, ya que es capaz de detectar los cambios en dichos modelos y son utilizados para generar migraciones en Django de forma automática para nuestra DB.

El ORM añade algunas simplicidades, como incluir una propiedad id de forma automática a nuestras tablas, que actúa de primary key, y que podemos utilizar en las consultas de la información, además incluye el alias pk a la primary key de la tabla para que podamos acceder fácilmente.

QuerySets

El otro componente principal son los QuerySets, estos son los objetos que representan una consulta en Django a la Base de Datos, desde la cual se puede recuperar, crear, borrar o actualizar la información de nuestra base de datos.

Estos objetos, tienen varias propiedades:

  • Son iterables: Todos los QuerySets pueden ser tratados como un listado de objetos que podemos recorrer.
  • Son Lazy: Los QuerySets no recuperan la información de la base de datos por defecto, si no que esperan a que esta sea evaluada.
  • Son Extendibles: Muchos de los métodos de un QuerySet devuelven otro QuerySet que podemos completar con más métodos.
  • Son versátiles: Los QuerySets pueden ser customizados a medida de nuestras necesidades, con casi ninguna limitación.

Para poder crear una nueva QuerySet y consultar la información es muy sencillo, solo tenemos que utilizar nuestros modelos, para decidir a qué información queremos acceder.

Empezando a hacer queries

A continuación, vamos a revisar cómo podemos hacer varias consultas con el ORM de Django y cules son sus principales métodos, además vamos a ver cómo se traducen estas consultas a su equivalente en SQL.

Recuperar toda la información

Django ORM

Person.objects.all()

El método all() recupera todo el contenido que tenemos guardado en nuestro modelo, al hacerlo de forma lazy, podemos aplicar principios de paginación para evitar traer toda la información en memoria. Person.objects.all()[:100] así traemos solo los 100 primeros resultados de la base de datos.

SQL

SELECT *
FROM person

Filtrar por nombre

Django ORM

Person.objects.filter(first_name='Juan', last_name='Hernández')

Django incluye el operador filter(), que nos permite filtrar las filas de nuestra tabla por varios parámetros, sin olvidar que cada parámetro que incluyamos será una sentencia AND adicional.

SQL

SELECT *
FROM person
WHERE first_name = 'Juan' AND last_name = 'Hernández'

Filtrar por todo nombre que empiece por J

Django ORM

Person.objects.filter(first_name__startwith='J')

Los parámetros que utilicemos en el operador filter() permiten que añadamos acciones, como el filtrado por un substring dentro de una cadena de texto, o comprobar si un número es mayor de otro, esto se hace utilizando los dobles: __.

SQL

SELECT *
FROM person
WHERE first_name like 'J%'

Contar todas las personas de la tabla

Django ORM

Person.objects.count()

El ORM de Django nos proporciona el método count() que nos permite contar todos los registros de la tabla. Este operador equivale al método COUNT de SQL.

SQL

SELECT COUNT(*)
FROM person

Ordenar registros

Django ORM

Person.objects.order_by('-age')

El operador (order_by())nos permite ordenar los registros que recuperemos, incluyendo el nombre de la columna como texto, le indicamos sobre que parámetro queremos ordenar, e incluyendo el signo - le decimos que queremos que sea orden descendente, si no ponemos ningún signo será como orden ascendente.

SQL

SELECT *
FROM person
ORDER BY 'AGE' DESC

Agregaciones de información

Django ORM

Person.objects.aggregate(max_age = Max('age'))

El operador aggregate() nos permite hacer operaciones de agregación sobre todos los registros de una tabla, por ejemplo, si queremos sumar las edades de todas las personas, o como en este caso si queremos obtener la edad máxima.

SQL

SELECT MAX(age)
FROM person

Agrupando la información

Django ORM

Person.objects.annotate(age_count = Count('age')).values('age_count', 'age')

# [{age_count: 10, age: 21}, {age_count:5, age: 22}...]

Django ORM no tiene un operador Group BY como podría ser de esperar, en lugar de eso cuando queremos agrupar información por un parámetro, tenemos que hacer una operación de agregación a nivel de fila con annotate(), en este caso contamos las personas que tienen la misma edad, y con esa operación conseguimos la agrupación.

Al acceder a la información utilizando el ORM, no podemos mapear el resultado a una entidad del modelo, y por eso tenemos que recuperar el diccionario de información en claro, con el operador values().

SQL

SELECT age, COUNT(age) as age_count
FROM person
GROUP BY age

Crear nuevos registros

Django ORM

Person.objects.create(first_name='Antonio', last_name='Hernandez', age=29)

La creación de nueva información en nuestros modelos se hace a través del operador create().

SQL

INSERT INTO person (first_name, last_name, age)
VALUES ('Antonio', 'Hernandez', 29);

Actualizar registros

Django ORM

Person.objects.filter(first_name='Antonio').update(first_name='Tony')

La actualización de información se hace con el operador update(), donde solo tenemos que especificar la información que queremos actualizar.

Cómo comentábamos podemos extender nuestras QuerySets con varios operadores, nuestro operador filter, esto nos permite concatenar varios operadores. Cuidado porque el operador update no devuelve otro QuerySet.

Una forma alternativa de actualizar un registro de nuestro modelo, es una vez tenemos nuestra instancia person, utilizar el método save(), después de modificarla.

SQL

UPDATE person 
SET first_name = 'Tony' 
WHERE first_name = 'Antonio'

Borrar registros

Django ORM

Person.objects.filter(age__gt=30).delete()

De la misma forma que tenemos los operadores update() y create() para insertar y actualizar información en nuestros modelos, tenemos el operador delete() para borrar uno o varios registros en nuestros modelos.

SQL

DELETE FROM person
WHERE age > 30

Exprimiendo las relaciones

Una de las virtudes de las bases de datos SQL, es su capacidad de relacionar información, ya que así es como podemos modelar la información en nuestra base de datos. Estas relaciones se hacen a través de lo que llamamos llaves foráneas.

Imagen 0 en Empezando a hacer queries con Django

Estas relaciones nos ayudan a separar cierta información que consideramos independiente en diferentes tablas, y aun así utilizarla para expandir la información que tenemos en nuestras tablas. Por ejemplo, en la imagen de arriba, tenemos la información de nuestros clientes, separada de la información de los pedidos que han hecho.

Pero aun así gracias a las relaciones podemos acceder a la información de los pedidos de nuestros clientes cuando consideremos oportuno y de forma muy sencilla.

Django Models con Relaciones

La creación de relaciones y claves foráneas en Django es mediante las clases ForeignKey, donde solo hay que especificar el modelo al que queremos referenciar, para crear la relación.

class Customer(models.Model):
    name = models.CharField(max_length=50)
    age = models.IntegerField()
    country = models.CharField(max_length=50)

class Order(models.Model):
    date = models.DateTime()
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    amount = models.IntegerField()

Accediendo a una relación

Django ORM

order = Order.objects.get(pk=1)
order.customer.name # Nombre del customer

Al recuperar nuestra fila order con id=1, ya somos capaces de acceder a la información del cliente relacionado con dicho order, este acceso lo hace Django de forma completamente transparente a nosotros.

Además, comentar que como hemos dicho anteriormente, las QuerySets son lazy, eso provoca que realmente Django haga 2 queries, una primera para recuperar el order con id=1 (SELECT * FROM order WHERE id =1), y una adicional para acceder a la información del customer, que es la que vemos abajo.

SQL

SELECT order.*, customer.name as name
FROM order
    LEFT JOIN customer ON order.customer_id = customer.id

Filtrando por una relación

Django ORM

Order.objects.filter(customer__age__gt = 30)

Una de las grandes ventajas de utilizar relaciones es poder acceder a la información de diferentes tablas cuando nos sea necesario, esto también aplicar cuando estamos buscando la información, Django nos permite hacer esto fácilmente utilizando la sintaxis de doble _, además como vemos en el ejemplo podemos concatenar el acceso a la información de nuestra relación con un operador de búsqueda como el __gt.

SQL

SELECT *
FROM order
    LEFT JOIN customer on order.customer_id = customer.id
WHERE customer.age > 30

Mejora las habilidades de tus desarrolladores
Acelera la formación tecnológica de tus equipos con OpenWebinars. Desarrolla tu estrategia de atracción, fidelización y crecimiento de tus profesionales con el menor esfuerzo.
Solicitar más información

Relación a la inversa

Django ORM

customer = Person.objects.get(pk=1)
orders = person.order_set.all() # Accediendo a todos los pedidos de un cliente

Algo muy destacable del ORM de Django es su clara sintaxis que facilita mucho la curva de aprendizaje, como podemos ver en este caso, cuando necesitamos seguir una relación a la inversa, es decir en lugar del modelo acceder desde el hijo al padre, es desde el padre al hijo. En ese caso solo es necesario una vez tengamos la instancia del padre, llamar a la propiedad “nombre del hijo”_set, y ya podremos utilizar cualquier operador visto para conseguir la información que necesitemos, además podemos customizar este nombre utilizando la propiedad related_name().

SQL

SELECT *
FROM order
WHERE order.customer_id = 1

Conclusión

El poder hacer búsquedas sobre la información que tenemos almacenadas es una de las grandes ventajas que existen en las bases de datos SQL, los ORMs pueden sernos tremendamente útiles siempre que sepamos cómo utilizarlos.

Django nos provee de un ORM para comunicarnos con nuestra base de datos y hacer queries de forma muy sencilla a la par que muy versátil y que es capaz de adaptarse a nuestras necesidades, haciendo que la gestión de migraciones, relaciones y buenas prácticas sea muy sencilla.

Compartir este post

También te puede interesar

Icono de la tecnología
Taller

Gestión de Formularios en Django

Intermedio
44 min.

Con este taller aprenderás a crear y definir nuestros propios formularios utilizando las herramientas que da Django, además...

Juan Benito Pacheco Rubio
4.6
Icono de la tecnología
Empresas

Arquitectura Hexagonal con Django

Intermedio
51 min.

Aprende con nuestro profesores los conceptos teóricos de arquitectura hexagonal, y cómo llevarla a la práctica en tus...

Juan Benito Pacheco Rubio
4.5