Domina las Queries en Django desde Cero

En Django, interactuar con la base de datos se hace principalmente a través de queries (consultas) utilizando el ORM (Object-Relational Mapping). Esto te permite trabajar con los datos de una manera más orientada a objetos, sin tener que escribir SQL manualmente.

Configuración de Modelos Base

A continuación, definimos algunos modelos fundamentales en Django con los que realizaremos queries esenciales para manipular y consultar datos de manera efectiva:

python
from django.db import models

class Estudiante(models.Model):
  nombre = models.CharField(max_length=100)
  edad = models.IntegerField()
  curso = models.CharField(max_length=50)
  promedio = models.FloatField()
  activo = models.BooleanField(default=True)

class Usuario(models.Model):
  nombre = models.CharField(max_length=100)
  email = models.EmailField(unique=True)
  edad = models.IntegerField()
  activo = models.BooleanField(default=True)

class Producto(models.Model):
  nombre = models.CharField(max_length=200)
  precio = models.DecimalField(max_digits=10, decimal_places=2)
  stock = models.IntegerField()
  categoria = models.CharField(max_length=100)

class Pedido(models.Model):
  usuario = models.ForeignKey(Usuario, on_delete=models.CASCADE)
  productos = models.ManyToManyField(Producto)
  fecha = models.DateTimeField(auto_now_add=True)
  total = models.DecimalField(max_digits=10, decimal_places=2)

Consultas Básicas


1.objects.all()

.objects.all() es un método de QuerySet en Django. Permite obtener todos los registros de un modelo de base de datos. Cuando lo llamas, Django construye y ejecuta una consulta SQL que recupera todas las filas de la tabla asociada al modelo.

Ejemplo 1: Obtener todos los usuarios

python
todos_usuarios = Usuario.objects.all()

Ejemplo 2: Imprimir nombres de todos los usuarios

python
for usuario in todos_usuarios:
  print(usuario.nombre)

Ejemplo 3: Convertir a lista

python
lista_usuarios = list(Usuario.objects.all())
2.objects.filter()

.objects.filter() es un método de QuerySet en Django que permite filtrar registros según condiciones específicas. Retorna un conjunto de registros que coinciden con los criterios de búsqueda definidos. Si no se encuentra ningún registro que cumpla con las condiciones, devuelve un QuerySet vacío.

python
usuarios_mayores = Usuario.objects.filter(edad__gte=18)

Ejemplo 1: Filtrar estudiantes mayores de 18

python
estudiantes_mayores = Estudiante.objects.filter(edad__gte=18)

Ejemplo 2: Filtrar por curso y promedio

python
estudiantes_matematicas = Estudiante.objects.filter(
  curso='Matemáticas', 
  promedio__gt=8.0
)

Ejemplo 3: Filtros combinados

python
estudiantes_activos_ingenieria = Estudiante.objects.filter(
  activo=True, 
  curso__startswith='Ingeniería'
)

Ejemplo 4: Filtros con múltiples condiciones

python
usuarios_activos = Usuario.objects.filter(
  activo=True, 
  edad__between=(18, 35)
)

Ejemplo 5: Filtrado con condiciones OR Cuando necesitas aplicar condiciones con el operador OR (es decir, al menos una de las condiciones debe cumplirse), debes usar el objeto Q en Django.

python
from django.db.models import Q

# El operador Q permite combinar condiciones usando los operadores lógicos como AND (&) y OR (|).
# En este caso, estamos filtrando usuarios cuya edad es mayor o igual a 18, 
# o bien aquellos que son empleados (es_empleado=True).

usuarios_especiales = Usuario.objects.filter(
  Q(edad__gte=18) | Q(es_empleado=True)
)


# En este ejemplo, se aplica un filtro usando el operador OR para obtener estudiantes
# que sean mayores o iguales a 18 años o cuyo promedio sea mayor a 9.0.

estudiantes_complejos = Estudiante.objects.filter(
  Q(edad__gte=18) | Q(promedio__gt=9.0)
)
3.objects.all().count()

.objects.all().count() es un método de QuerySet en Django que devuelve el número total de registros en una tabla asociada al modelo. Este método ejecuta una consulta SQL para contar todas las filas de la tabla y devuelve un número entero.

Ejemplo 1: Contar total de estudiantes

python
total_estudiantes = Estudiante.objects.all().count()

Ejemplo 2: Contar estudiantes por condición

python
estudiantes_mayores_count = Estudiante.objects.filter(edad__gte=18).count()
  Nota

  • __gte: Se utiliza para filtrar registros mayores o iguales a un valor dado. Ejemplo: edad__gte=18 devolvería registros donde la edad es mayor o igual a 18 años.

  • __lte: Se usa para filtrar registros menores o iguales a un valor dado. Ejemplo: edad__lte=18 devolvería registros donde la edad es menor o igual a 18 años.

Ejemplo 3: Contar estudiantes por curso

python
count_matematicas = Estudiante.objects.filter(curso='Matemáticas').count()
4.objects.all().select_related()

.objects.all().select_related() es un método de QuerySet en Django que optimiza las consultas cuando hay relaciones de clave foránea. Utiliza un solo JOIN SQL para recuperar los objetos relacionados, reduciendo el número de consultas a la base de datos y mejorando el rendimiento al acceder a datos relacionados.

Ejemplo 1: Consultar cursos con estudiantes relacionados

python
cursos_con_estudiantes = Curso.objects.select_related('estudiantes')

Ejemplo 2: Consulta con múltiples relaciones

python
detalles_curso = Curso.objects.select_related('estudiantes__curso')

Ejemplo 3: Prefetch relacionado

python
cursos_detallados = Curso.objects.prefetch_related('estudiantes')
5.objects.get()

.objects.get() es un método de QuerySet en Django que se utiliza para obtener un único registro que coincida con los criterios especificados. Si se encuentran múltiples registros que coinciden, o si no se encuentra ninguno, se lanzará una excepción (MultipleObjectsReturned o DoesNotExist).

Ejemplo 1: Obtener estudiante por ID

python
estudiante = Estudiante.objects.get(id=1)

Ejemplo 2: Obtener por condición única

python
estudiante_juan = Estudiante.objects.get(nombre='Juan')

Ejemplo 3: Obtener con múltiples condiciones

python
estudiante_especifico = Estudiante.objects.get(
  nombre='María', 
  curso='Matemáticas'
)
6.objects.get().only()

.objects.get().only() es un método en Django que permite recuperar un único registro y limitar los campos que se cargan en la consulta. Esto optimiza el rendimiento al evitar la carga de columnas innecesarias. Se utiliza junto con .get() para obtener un único objeto, pero solo con los campos especificados.

Ejemplo 1: Seleccionar solo nombre y edad

python
estudiantes_lite = Estudiante.objects.only('nombre', 'edad')

Ejemplo 2: Usar en iteración

python
for estudiante in estudiantes_lite:
  print(f"{estudiante.nombre} - {estudiante.edad}")

Ejemplo 3: Consulta con condición

python
estudiantes_select = Estudiante.objects.filter(
  activo=True
).only('nombre', 'promedio')
7.objects.get().defer()

.objects.get().defer() es un método en Django que permite recuperar un único registro y excluir ciertos campos de la consulta, cargando solo los campos que no se han diferido. Es útil cuando necesitas optimizar la consulta evitando la carga de columnas específicas que no se utilizarán.

Ejemplo 1: Defer campo promedio

python
estudiantes_sin_promedio = Estudiante.objects.defer('promedio')

Ejemplo 2: Defer múltiples campos

python
estudiantes_basico = Estudiante.objects.defer('promedio', 'curso')

Ejemplo 3: Defer con filtro

python
estudiantes_activos_basico = Estudiante.objects.filter(
  activo=True
).defer('promedio')
8.exclude()

Permite filtrar los registros excluyendo aquellos que coincidan con las condiciones especificadas. Es lo opuesto a .filter().

Ejemplo: Obtener todos los estudiantes cuyo promedio no sea mayor a 7

python
estudiantes = Estudiante.objects.exclude(promedio__gt=7)

# Excluir para obtener todos los usuarios activos
usuarios_no_activos = Usuario.objects.exclude(activo=True)
9.order_by()

Permite ordenar los resultados según un campo específico. Puedes usar - para invertir el orden.

Ejemplo: Ordenar los usuarios por edad descendente

python
usuarios = Usuario.objects.order_by('-edad')

Ejemplos:

python
# Ordenar por un campo en orden ascendente (por defecto)
estudiantes_ordenados = Estudiante.objects.order_by('edad')


# Ordenar por un campo en orden descendente
usuarios_desc = Usuario.objects.order_by('-edad')


# Ordenación múltiple: primero por edad, luego por nombre
usuarios_multiples = Usuario.objects.order_by('edad', 'nombre')


# Ordenar por un campo en orden descendente (edad) y luego ascendente (nombre)
usuarios_ordenados = Usuario.objects.order_by('-edad', 'nombre')
10.distinct()

Elimina los registros duplicados de los resultados de la consulta.

Ejemplo: Obtener categorías únicas de productos

python
categorias = Producto.objects.values('categoria').distinct()
11.values()

Permite obtener un diccionario de los campos especificados en lugar de instancias del modelo.

Ejemplo: Obtener solo los nombres y correos de los usuarios

python
usuarios = Usuario.objects.values('nombre', 'email')
12.values_list()

Similar a .values(), pero devuelve una lista de tuplas en lugar de diccionarios.

Ejemplo: Obtener una lista de tuplas con nombre y edad de los usuarios

python
usuarios = Usuario.objects.values_list('nombre', 'edad')
13.aggregate()

Permite realizar cálculos agregados como sum(), count(), Max, Min, avg() sobre un conjunto de resultados.

Ejemplo 1: Obtener el promedio de edad de todos los usuarios

python
from django.db.models import Avg, Max, Min, Sum, Count

# Calcular el promedio de edad
promedio_edad = Usuario.objects.aggregate(Avg('edad'))

# Realizar múltiples agregaciones
estadisticas = Usuario.objects.aggregate(
  promedio_edad=Avg('edad'),         # Promedio de edad
  edad_maxima=Max('edad'),           # Edad máxima
  total_usuarios=Count('id')        # Total de usuarios
)

Ejemplo 1: Promedio de edad

python
edad_promedio = Usuario.objects.aggregate(Avg('edad'))

Ejemplo 2: Suma total de productos

python
total_productos = Producto.objects.aggregate(
  total_stock=Sum('stock')
)

Ejemplo 3: Precio máximo y mínimo

python
precios_producto = Producto.objects.aggregate(
  precio_max=Max('precio'),
  precio_min=Min('precio')
)
14.annotate()

Permite agregar información adicional a cada objeto, como un valor calculado.

Ejemplo: Obtener el número de productos por cada pedido

python
from django.db.models import Count
pedidos = Pedido.objects.annotate(num_productos=Count('productos'))
15.first() y .last()

Obtienen el primer o último registro de una consulta.

Ejemplo: Obtener el primer y el último usuario

python
primer_usuario = Usuario.objects.first()  # Obtiene el primer usuario
ultimo_usuario = Usuario.objects.last()   # Obtiene el último usuario
16Consultas con Relaciones en Django`

En Django, puedes realizar consultas sobre relaciones entre modelos, como relaciones de clave foránea (ForeignKey) o relaciones muchos a muchos (ManyToMany). Aquí te mostramos algunos ejemplos de cómo trabajar con estas relaciones.

Ejemplo 1: Consultas con relaciones de clave foránea (ForeignKey)

python
pedidos_usuario = Pedido.objects.filter(usuario__nombre='Juan')

Ejemplo 2: Consultas con relaciones many-to-many

python
productos_pedido = Producto.objects.filter(pedido__usuario__nombre='Maria')

Ejemplo 3: **Subconsultas

python
from django.db.models import Subquery, OuterRef

usuarios_con_pedidos = Usuario.objects.filter(
  id__in=Pedido.objects.filter(
      total__gt=100
  ).values('usuario')
)

Limitar Resultados

Para limitar la cantidad de resultados obtenidos en una consulta, puedes usar la notación de corte ([:n]), donde n es el número máximo de registros que deseas obtener.

Ejemplo 1: Obtener los 5 primeros usuarios

python
top_5_usuarios = Usuario.objects.all()[:5]

Ejemplo 2: Obtener los últimos 5 usuarios (ordenados por fecha de creación)

python
ultimos_5_usuarios = Usuario.objects.all().order_by('-fecha_creacion')[:5]

Ejemplo 3: Limitar el número de productos en stock

python
productos_en_stock = Producto.objects.filter(stock__gt=0)[:10]

Métodos de Relaciones en Django

En Django, los modelos relacionados se pueden acceder de forma sencilla usando los métodos que Django genera automáticamente para las relaciones de clave foránea o ManyToMany.

Ejemplo 1: Acceder a registros relacionados Cuando se tiene una relación ForeignKey, Django crea automáticamente un manager relacionado para acceder a los objetos relacionados.

python
class Pedido(models.Model):
  usuario = models.ForeignKey(Usuario, on_delete=models.CASCADE)

# Obtener todos los pedidos de un usuario
pedidos_usuario = usuario.pedido_set.all()

Ejemplo 2: Filtrar registros relacionados Se pueden aplicar filtros a través de las relaciones utilizando __ para acceder a los campos de los modelos relacionados.

Filtrar pedidos de usuarios con edad mayor o igual a 18

python
pedidos_filtrados = Pedido.objects.filter(usuario__edad__gte=18)

En este ejemplo, estamos filtrando los pedidos para obtener solo aquellos que pertenecen a usuarios con una edad de 18 años o más.