En Django, una vez definidos los modelos de datos, el paso siguiente es interactuar con la base de datos para crear, recuperar, actualizar o eliminar registros. Esta documentación describe la API de consulta derivada de los modelos, conocida como QuerySets, y cómo gestionar objetos.
Para ilustrar los ejemplos, consideraremos una aplicación de encuestas con los siguientes modelos:
class Survey(models.Model):
url_slug = models.SlugField(unique_for_month='publish_date')
title = models.CharField(max_length=255)
publish_date = models.DateTimeField()
end_date = models.DateTimeField()
def __str__(self):
return self.title
class Meta:
get_latest_by = 'publish_date'
class Option(models.Model):
survey = models.ForeignKey(Survey, related_name='options')
option_text = models.CharField(max_length=255)
vote_count = models.IntegerField(default=0)
def __str__(self):
return self.option_text
La API de consulta se basa en la construcción de conjuntos de resultados, conocidos como QuerySets. Estos conjuntos son indepandientes de la base de datos y representan una colección de objetos que cumplen ciertos criterios. Son perezosos: la consulta a la base de datos no se ejecuta hasta que los datos son realmente necesarios.
Construyendo consultas
Cada modelo de Django posee un gestor (manager), objects, que actúa como la puerta de entrada a los registros. Survey.objects proporciona un punto de partida para todas las consultas sobre encuestas. Para obtener un conjunto de resultados evaluable, se utiliza el método all().
all_surveys = Survey.objects.all()
Filtrado y exclusión
Para seleccionar un subconjunto de datos, se aplican filtros. Los métodos principales son:
filter(**kwargs): Retorna un nuevo conjunto de resultados que coincide con los parámetros.exclude(**kwargs): Retorna un nuevo conjunto de resultados que NO coincide con los parámetros.
Estos métodos retornan nuevos conjuntos de resultados, lo que permite encadenar múltiples operaciones. Los conjuntos intermedios no se modifican. La consulta a la base de datos se ejecuta solo al evaluar (consumir) el conjunto final.
# Encadenamiento de filtros
recent_survey = Survey.objects.filter(
title__startswith="Encuesta"
).exclude(
end_date__lte=now()
).filter(
publish_date__gte=datetime(2023, 1, 1)
)
# Cada paso genera un nuevo conjunto
set1 = Survey.objects.filter(title__startswith="Encuesta")
set2 = set1.exclude(end_date__lte=now())
Búsqueda por campos
Las búsquedas se realizan utilizando la sintaxis campo__tipo_de_búsqueda. Ejemplos:
# Filtro por fecha
Survey.objects.filter(publish_date__lte=now())
# Traducción SQL: SELECT * FROM app_survey WHERE publish_date <= NOW();
La tabla siguiente describe los tipos de búsqueda soportados:
| Tipo | Descripción |
|---|---|
exact |
Coincidencia exacta. Es el valor por defecto. |
iexact |
Coincidencia exacta sin importar mayúsculas. |
contains |
Coincidencia contenida (sensible a mayúsculas). |
icontains |
Coincidencia contenida (insensible a mayúsculas). |
gt, gte |
Mayor que, mayor o igual que. |
lt, lte |
Menor que, menor o igual que. |
in |
Valor dentro de una lista dada. |
startswith, istartswith |
Empieza con (sensible/insensible a mayúsculas). |
year, month, day |
Filtrar componentes de fecha. |
isnull |
Valor es o no es NULL. |
range |
Entre dos valores (incluidos). |
Los parámetros múltiples se combinan con AND. Para consultas complejas con OR, se utilizan objetos Q.
from django.db.models import Q
# Consulta con OR
complex_query = Survey.objects.filter(
Q(title__startswith="Encuesta") | Q(title__startswith="Votación")
)
# Combinación de Q y kwargs (kwargs van después)
Survey.objects.get(
Q(publish_date=date(2023, 5, 1)) | Q(publish_date=date(2023, 5, 10)),
title__startswith="Encuesta"
)
Obteniendo resultados
Para extraer los datos de un conjunto de resultados, se itera sobre él, se realizan cortes (slicing) o se usan métodos específicos.
# Iteración
for encuesta in Survey.objects.all():
print(encuesta)
# Cortes
quinta_encuesta = Survey.objects.all()[4]
todas_menos_dos = Survey.objects.all()[2:]
Los conjuntos de resultados mantienen una caché. La primera evaluación consulta la base de datos y almacena los resultados. Evaluaciones posteriores del mismo conjunto usan la caché. Esto es importante para el rendimiento.
Métodos de evaluación específicos
get(**kwargs): Devuelve el único objeto que cumple los criterios. Lanza excepciones si no hay coincidencias o hay más de una.count(): Retorna el número de objetos en el conjunto.in_bulk(id_list): Dado una lista de IDs, retorna un diccionario {id: objeto}.latest(field_name=None): Devuelve el objeto más reciente según un campo de fecha (usualmente definido en el Meta del modelo).
Relaciones entre modelos
Django gestiona automáticamente las relaciones (ForeignKey, OneToOneField, ManyToManyField) mediante descriptores.
Acceso a objetos relacionados
Dado un objeto, se puede acceder a sus relacionados directamente. Por ejemplo, si mi_opcion es una instancia de Option, mi_opcion.survey retorna la Survey asociada.
La navegación también es posible en consultas con unión implícita:
# Encuestas cuyas opciones contengan "Sí"
Survey.objects.filter(options__option_text__icontains="Sí")
Conjuntos inversos (reverse lookups)
Para relaciones como ForeignKey, el modelo relacionado (Survey) obtiene automáticamente un gestor inverso. Si encuesta es una instancia de Survey:
# Todas las opciones de una encuesta
todas_opciones = encuesta.options.all() # Usa el related_name='options'
# o, por defecto: encuesta.option_set.all()
Estos gestores inversos disponen de métodos útiles como create(), count(), y los métodos de filtro estándar.
Modificadores avanzados del conjunto de resultados
Ordenamiento: order_by(*fields)
Survey.objects.filter(publish_date__year=2023).order_by('-publish_date', 'title')
# Ordena por fecha descendente, luego título ascendente. '-' indica desc.
Duplicados: distinct()
Elimina filas duplicadas del resultado, equivalente a SELECT DISTINCT.
Retorno como diccionarios: values(*fields)
En lugar de instancias de modelo, retorna una lista de diccionarios. Útil para obtener solo algunos campos de manera eficiente.
datos = Survey.objects.filter(publish_date__year=2023).values('id', 'title', 'publish_date')
# [{'id': 1, 'title': '...', 'publish_date': ...}, ...]
Optimización de consultas relacionales: select_related()
Realiza una unión SQL (JOIN) y almacena en caché los objetos relacionados en una sola consulta. Ideal para relaciones ForeignKey y OneToOneField cuando se sabe que se accederá a los datos relacionados.
# Obtiene la opción y su encuesta en una sola consulta
opcion_con_encuesta = Option.objects.select_related('survey').get(id=1)
# opcion_con_encuesta.survey NO causará otra consulta a la BD.
SQL explícito con extra()
Para casos donde la ORM no es suficiente, extra() permite inyectar cláusulas SQL adicionales (select, where, tables). Su uso reduce la portabilidad del código.
Survey.objects.extra(
select={'total_opciones': 'SELECT COUNT(*) FROM app_option WHERE app_option.survey_id = app_survey.id'}
)
Creación, actualización y eliminación
Crear objetos
Instanciar el modelo y llamar a save() ejecuta un INSERT.
nueva_encuesta = Survey(
url_slug="mi-encuesta",
title="Nueva encuesta",
publish_date=now(),
end_date=now() + timedelta(days=7)
)
nueva_encuesta.save()
# Creación mediante el gestor inverso
nueva_encuesta.options.create(option_text="Opción A", vote_count=0)
Actualizar objetos
Modificar atributos de una instancia existente y llamar a save() ejecuta un UPDATE.
encuesta = Survey.objects.get(id=1)
encuesta.title = "Título actualizado"
encuesta.save()
Eliminar objetos
El método delete() elimina el objeto de la base de datos de forma inmediata. Se puede usar en un solo objeto o en un conjunto de resultados completo.
# Eliminar una encuesta
encuesta_a_eliminar = Survey.objects.get(id=10)
encuesta_a_eliminar.delete()
# Eliminar todas las encuestas de un año (CUIDADO)
Survey.objects.filter(publish_date__year=2020).delete()
# Equivalente a: Survey.objects.all().delete() si no hay filtro.
Otros métodos útiles del modelo
get_FOO_display(): Para campos conchoices, retorna la representación legible del valor.get_next_by_FOO()/get_previous_by_FOO(): Para campos de fecha, obtiene el objeto siguiente/anterior en el orden cronológico.- Métodos generados para
FileFieldeImageField: comoget_FOO_url(),get_FOO_size(), etc.