Django
Tutorial Django: Operaciones CRUD y consultas
Django: Aprende a realizar operaciones CRUD esenciales usando el ORM. Manipula datos fácilmente sin SQL manual.
Aprende Django GRATIS y certifícateOperaciones CRUD
Las operaciones CRUD (Crear, Leer, Actualizar y Borrar) son esenciales para la manipulación de datos en aplicaciones Django. El ORM (Object-Relational Mapper) de Django permite realizar estas operaciones y sin necesidad de escribir consultas SQL manualmente.
Para crear un nuevo registro en la base de datos, se instancia un objeto del modelo correspondiente y se utiliza el método save()
para guardarlo:
# Crear un nuevo autor
autor = Autor(nombre='Miguel de Cervantes', edad=68)
autor.save()
Otra opción es utilizar el método objects.create()
para crear y guardar el objeto en una sola línea:
# Crear y guardar un autor
autor = Autor.objects.create(nombre='Isabel Allende', edad=79)
La lectura de registros se realiza mediante QuerySets, que permiten obtener datos de la base de datos:
# Obtener todos los autores
autores = Autor.objects.all()
# Obtener un autor específico
autor = Autor.objects.get(id=1)
Es importante manejar excepciones como DoesNotExist
y MultipleObjectsReturned
al utilizar get()
:
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
try:
autor = Autor.objects.get(nombre='Gabriel García Márquez')
except ObjectDoesNotExist:
# El autor no existe
pass
except MultipleObjectsReturned:
# Hay múltiples autores con ese nombre
pass
Para actualizar un registro existente, se modifican sus atributos y se llama nuevamente al método save()
:
# Actualizar la edad de un autor
autor = Autor.objects.get(nombre='Isabel Allende')
autor.edad = 80
autor.save()
Si se desea actualizar múltiples registros a la vez, se utiliza el método update()
sobre un QuerySet:
from django.db.models import F
# Incrementar en 1 la edad de todos los autores
Autor.objects.all().update(edad=F('edad') + 1)
El uso de F()
permite referenciar el valor actual del campo en la base de datos, facilitando operaciones aritméticas.
Para borrar registros, se utiliza el método delete()
tanto en objetos individuales como en QuerySets:
# Borrar un autor específico
autor = Autor.objects.get(nombre='Miguel de Cervantes')
autor.delete()
# Borrar autores menores de 40 años
Autor.objects.filter(edad__lt=40).delete()
Es fundamental tener precaución al eliminar datos, ya que esta acción es irreversible y puede afectar la integridad de la información almacenada.
Las operaciones pueden agruparse dentro de transacciones para asegurar su atomicidad y mantener la consistencia en caso de errores:
from django.db import transaction
with transaction.atomic():
autor = Autor.objects.create(nombre='Julio Cortázar', edad=70)
libro = Libro.objects.create(titulo='Rayuela', autor=autor)
El uso de transacciones garantiza que, si ocurre una excepción dentro del bloque, todos los cambios realizados se reviertan, evitando estados intermedios inconsistentes en la base de datos.
Búsquedas y filtrado
Las consultas en Django permiten obtener información específica de la base de datos aplicando filtros y búsquedas avanzadas. El ORM de Django proporciona una API rica para filtrar QuerySets y realizar consultas complejas.
Para filtrar registros, se utiliza el método filter()
sobre un QuerySet. Este método acepta parámetros basados en los nombres de los campos del modelo y soporta lookups para especificar condiciones más detalladas:
# Obtener autores con edad mayor a 50
autores_maduros = Autor.objects.filter(edad__gt=50)
# Obtener libros publicados en el año 2020
libros_2020 = Libro.objects.filter(fecha_publicacion__year=2020)
# Obtener artículos cuyo título contiene la palabra 'Django'
articulos_django = Articulo.objects.filter(titulo__icontains='django')
Los lookups de campo permiten utilizar operadores como __gt
(mayor que), __lt
(menor que), __gte
(mayor o igual que), __lte
(menor o igual que), __exact
(igual a) y __iexact
(igual a sin mayúsculas), entre otros. Estos lookups amplían la capacidad de búsqueda más allá de la igualdad exacta.
Para excluir registros que cumplan ciertas condiciones, se emplea el método exclude()
:
# Obtener autores que no sean menores de 30 años
autores_adultos = Autor.objects.exclude(edad__lt=30)
# Obtener libros que no estén en categoría 'Ficción'
libros_no_ficcion = Libro.objects.exclude(categoria='Ficción')
Es posible encadenar múltiples filtros para refinar aún más los resultados. Cada filtro adicional se aplica sobre el QuerySet resultante del anterior:
# Obtener autores españoles mayores de 40 años
autores_espanoles = Autor.objects.filter(pais='España', edad__gt=40)
# Obtener libros de ciencia ficción publicados después de 2015
libros_cf_recientes = Libro.objects.filter(categoria='Ciencia Ficción').filter(fecha_publicacion__year__gt=2015)
Django también permite realizar consultas complejas utilizando el objeto Q
, que facilita la construcción de condiciones con operadores lógicos como AND y OR:
from django.db.models import Q
# Obtener autores que sean de España o tengan más de 60 años
autores_espanoles_o_mayores = Autor.objects.filter(Q(pais='España') | Q(edad__gt=60))
# Obtener libros cuyo título contenga 'Python' y que no sean de categoría 'Educativo'
libros_python = Libro.objects.filter(Q(titulo__icontains='python') & ~Q(categoria='Educativo'))
El uso de Q
es especialmente útil cuando se requiere combinar condiciones de manera más flexible que con múltiples llamadas a filter()
.
Para acceder a campos de modelos relacionados, se utilizan las doble subrayas (__
) para seguir relaciones de clave foránea:
# Obtener libros cuyo autor tenga más de 50 años
libros_autores_maduros = Libro.objects.filter(autor__edad__gt=50)
# Obtener autores que tengan libros en categoría 'Historia'
autores_historia = Autor.objects.filter(libro__categoria='Historia').distinct()
Es importante utilizar distinct()
cuando una consulta pueda devolver resultados duplicados debido a relaciones ManyToMany o OneToMany.
Los métodos especiales como values()
y values_list()
permiten obtener diccionarios o listas con valores específicos, optimizando el rendimiento al evitar cargar objetos completos:
# Obtener una lista de nombres de autores
nombres_autores = Autor.objects.values_list('nombre', flat=True)
# Obtener diccionarios con nombre y edad de autores
datos_autores = Autor.objects.values('nombre', 'edad')
Para realizar búsquedas más avanzadas, Django soporta expresiones regulares con el lookup __regex
:
# Obtener libros cuyo ISBN siga un patrón específico
libros_isbn = Libro.objects.filter(isbn__regex=r'^978-3-16')
Al trabajar con grandes volúmenes de datos, es recomendable limitar el número de registros devueltos utilizando slicing o el método limit()
, aunque este último es más común en SQL directamente:
# Obtener los primeros 10 autores ordenados por edad descendente
autores_mayores = Autor.objects.all().order_by('-edad')[:10]
Las consultas en Django son vagas, es decir, no se ejecutan hasta que se evalúan. Esto permite construir consultas sin acceder a la base de datos hasta el momento necesario.
Al depurar u optimizar consultas, puede ser útil ver el SQL generado por el ORM:
autores = Autor.objects.filter(edad__gt=50)
print(autores.query)
Para consultas más complejas, se pueden utilizar anotaciones y agregaciones con annotate()
y aggregate()
, aprovechando funciones de agregación como Count
, Sum
, Avg
, etc.:
from django.db.models import Count, Avg
# Obtener el número de libros por autor
autores_libros = Autor.objects.annotate(num_libros=Count('libro'))
# Obtener la edad promedio de los autores
edad_promedio = Autor.objects.aggregate(promedio_edad=Avg('edad'))
Estas herramientas permiten realizar cálculos y obtener estadísticas directamente desde la base de datos.
Ordenación y paginación
La ordenación y la paginación son herramientas esenciales para manejar grandes conjuntos de datos en Django. Utilizando el ORM de Django, es posible ordenar los resultados y dividirlos en páginas de forma sencilla y optimizada.
Para ordenar un QuerySet, se utiliza el método order_by()
, especificando el nombre de los campos por los cuales se desea ordenar. Se pueden ordenar los resultados en orden ascendente o descendente:
# Ordenar autores por nombre en orden ascendente
autores = Autor.objects.all().order_by('nombre')
# Ordenar libros por fecha de publicación en orden descendente
libros = Libro.objects.all().order_by('-fecha_publicacion')
El uso de un signo menos -
antes del nombre del campo indica que la ordenación será en orden descendente. Sin él, la ordenación es ascendente por defecto.
Es posible ordenar por múltiples campos, especificándolos en el orden de prioridad:
# Ordenar autores por país y luego por edad
autores = Autor.objects.all().order_by('pais', 'edad')
# Ordenar libros primero por categoría descendente y luego por título ascendente
libros = Libro.objects.all().order_by('-categoria', 'titulo')
Al ordenar por campos de modelos relacionados, se utilizan las dobles subrayas __
para seguir las relaciones:
# Ordenar libros por el nombre del autor
libros = Libro.objects.all().order_by('autor__nombre')
La paginación permite dividir un conjunto grande de resultados en partes más manejables. Una forma básica de paginar es utilizando el slicing de QuerySets:
# Obtener los primeros 10 autores
primeros_autores = Autor.objects.all()[:10]
# Obtener los siguientes 10 autores (del 11 al 20)
siguientes_autores = Autor.objects.all()[10:20]
Sin embargo, para una paginación más robusta, Django proporciona la clase Paginator
en el módulo django.core.paginator
. Esta clase facilita la creación de páginas y el manejo del número total de objetos y páginas.
from django.core.paginator import Paginator
# Suponiendo una lista de todos los libros
libros = Libro.objects.all()
# Crear un paginator con 25 libros por página
paginator = Paginator(libros, 25)
# Acceder a la primera página de libros
pagina1 = paginator.page(1)
libros_pagina1 = pagina1.object_list
El objeto Paginator
proporciona atributos útiles como num_pages
para obtener el número total de páginas, y métodos para acceder a páginas específicas.
Es importante manejar excepciones como EmptyPage
e InvalidPage
al acceder a páginas fuera del rango disponible:
from django.core.paginator import EmptyPage, PageNotAnInteger
try:
pagina_numero = 3
pagina = paginator.page(pagina_numero)
except PageNotAnInteger:
# Si la página no es un entero, mostrar la primera página
pagina = paginator.page(1)
except EmptyPage:
# Si la página está fuera de rango, mostrar la última página
pagina = paginator.page(paginator.num_pages)
La paginación es especialmente útil cuando se combinan resultados ordenados y filtrados. Por ejemplo, para mostrar una lista de artículos ordenados por fecha y paginados:
# Filtrar artículos publicados
articulos_publicados = Articulo.objects.filter(publicado=True).order_by('-fecha_publicacion')
# Paginación de los artículos
paginator = Paginator(articulos_publicados, 10)
pagina_actual = paginator.page(1)
Al trabajar con paginación, es recomendable optimizar las consultas para mejorar el rendimiento. El uso de lazy loading en los QuerySets asegura que solo se carguen los datos necesarios.
Al ordenar y paginar conjuntos de datos grandes, se debe considerar el impacto en la performance de la aplicación. El uso de índices en la base de datos para los campos por los que se ordena frecuentemente puede mejorar significativamente el tiempo de respuesta.
También es posible utilizar métodos como select_related()
y prefetch_related()
para reducir el número de consultas al acceder a campos de modelos relacionados:
# Optimizar la consulta al ordenar por campos relacionados
libros = Libro.objects.select_related('autor').order_by('autor__nombre')
# Paginación de libros con autores preseleccionados
paginator = Paginator(libros, 20)
La paginación basada en cursor es otra técnica avanzada que puede mejorar la eficiencia en aplicaciones con paginación intensiva. Aunque Django no la implementa por defecto, es posible utilizar paquetes externos o implementar soluciones personalizadas.
Para implementar ordenación dinámica basada en parámetros de usuario, se debe sanitizar las entradas para prevenir inyecciones SQL. Es preferible usar listas de campos permitidos:
# Campos de ordenación permitidos
campos_permitidos = ['nombre', 'fecha_publicacion', 'categoria']
campo_orden = request.GET.get('orden') # Supongamos que es 'nombre'
if campo_orden in campos_permitidos:
libros = Libro.objects.all().order_by(campo_orden)
else:
libros = Libro.objects.all()
Esto asegura que solo se utilicen campos seguros para la ordenación, evitando posibles vulnerabilidades.
La ordenación y paginación en Django son herramientas que mejoran la usabilidad y el rendimiento de las aplicaciones web al manejar los datos presentados a los usuarios.
Buenas prácticas en consultas
Al desarrollar aplicaciones con Django, es fundamental seguir buenas prácticas en consultas para optimizar el rendimiento y garantizar la eficiencia en el acceso a la base de datos. A continuación, se presentan recomendaciones clave para mejorar las consultas utilizando el ORM de Django.
Uno de los aspectos más importantes es minimizar el número de consultas a la base de datos. El uso de select_related()
y prefetch_related()
permite reducir las consultas adicionales al obtener objetos relacionados:
# Utilizar select_related para relaciones OneToOne o ForeignKey
libros = Libro.objects.select_related('autor').all()
# Utilizar prefetch_related para relaciones ManyToMany o reverse ForeignKey
autores = Autor.objects.prefetch_related('libros').all()
Estas funciones cargan de manera los datos relacionados, evitando el problema conocido como N+1 consultas, donde se realiza una consulta adicional por cada objeto en un conjunto.
Es esencial ser consciente de cuándo se evalúan los QuerySets, ya que son evaluados de forma perezosa (lazy evaluation). Para evitar evaluaciones innecesarias, se deben encadenar operaciones antes de acceder a los resultados:
# Encadenar filtrados y anotaciones antes de evaluar el QuerySet
autores_populares = Autor.objects.filter(activo=True).annotate(num_libros=Count('libro'))
# Evaluación al iterar sobre el QuerySet
for autor in autores_populares:
print(autor.nombre, autor.num_libros)
Evitar iteraciones dentro de bucles que accedan a la base de datos en cada iteración es crucial. En lugar de ello, realizar operaciones en conjuntos:
# Evitar consultas dentro de bucles
for libro in libros:
comentarios = libro.comentario_set.all() # Consulta en cada iteración
# Solución óptima con prefetch_related
libros = Libro.objects.prefetch_related('comentarios').all()
for libro in libros:
comentarios = libro.comentarios.all() # Ya cargados en memoria
Utilizar expresiones F y expresiones combinadas mejora la eficiencia al permitir operaciones directas en la base de datos sin cargar objetos en memoria:
from django.db.models import F
# Incrementar la cantidad de ventas de un libro
Libro.objects.filter(id=1).update(ventas=F('ventas') + 1)
Las transacciones garantizan la integridad de los datos en operaciones complejas. Utilizar transaction.atomic()
asegura que un bloque de código sea atómico:
from django.db import transaction
with transaction.atomic():
pedido = Pedido.objects.create(usuario=usuario)
for item in carrito:
DetallePedido.objects.create(pedido=pedido, producto=item.producto, cantidad=item.cantidad)
Esta práctica previene inconsistencias en caso de que ocurra una excepción durante la ejecución del bloque transaccional.
La selección de los campos necesarios con only()
o defer()
optimiza el uso de memoria y reduce la carga de datos innecesarios:
# Obtener solo el título y autor de los libros
libros = Libro.objects.only('titulo', 'autor')
# O diferir campos pesados
libros = Libro.objects.defer('descripcion_larga')
Al trabajar con grandes conjuntos de datos, es recomendable utilizar generadores y el método iterator()
para manejar la memoria:
# Iterar sobre un gran QuerySet sin cargar todo en memoria
for libro in Libro.objects.all().iterator():
procesar_libro(libro)
El uso adecuado de índices en los modelos mejora el rendimiento en consultas frecuentes. Definir índices en campos utilizados para filtrado y ordenación:
class Libro(models.Model):
titulo = models.CharField(max_length=200, db_index=True)
fecha_publicacion = models.DateField()
class Meta:
indexes = [
models.Index(fields=['fecha_publicacion']),
]
Es vital revisar las consultas generadas por el ORM para identificar posibles optimizaciones. Utilizar la función query
en los QuerySets:
# Revisar la consulta SQL generada
query = Libro.objects.filter(autor__nombre='Jane Austen')
print(query.query)
Para operaciones complejas que no son eficientes con el ORM, considerar el uso de consultas crudas con raw()
o ejecutar consultas SQL personalizadas, siempre teniendo en cuenta la seguridad contra inyecciones SQL:
# Consulta cruda segura con parámetros
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM mi_app_libro WHERE ventas > %s", [1000])
resultados = cursor.fetchall()
Al utilizar agregaciones y anotaciones, es importante comprender cómo afectan al rendimiento y asegurarse de que los índices soportan estas operaciones. Evitar sobrecargar las consultas con múltiples agregaciones si no es necesario.
Implementar caché en consultas que se ejecutan frecuentemente puede mejorar significativamente el rendimiento. Django ofrece mecanismos de caché fáciles de integrar:
from django.core.cache import cache
# Intentar obtener los libros del caché
libros = cache.get('libros_populares')
if not libros:
# Si no están en caché, realizar la consulta y almacenarla
libros = Libro.objects.filter(ventas__gt=1000)
cache.set('libros_populares', libros, 300) # Caché por 5 minutos
Para garantizar la escalabilidad, es recomendable limitar el uso de operaciones que requieren un procesamiento intensivo en la base de datos, como consultas con múltiples JOIN
o subconsultas anidadas.
Mantener un código limpio y legible en las consultas facilita el mantenimiento y reduce la probabilidad de errores. Evitar lógica compleja dentro de las consultas y, en su lugar, dividir en funciones o métodos auxiliares cuando sea apropiado.
Todas las lecciones de Django
Accede a todas las lecciones de Django y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Django
Introducción Y Entorno
Instalación Y Configuración Django Con Venv
Introducción Y Entorno
Arquitectura De Un Proyecto Django
Introducción Y Entorno
Base De Datos Mysql En Django
Modelos Y Base De Datos
Creación De Modelos
Modelos Y Base De Datos
Asociaciones De Modelos
Modelos Y Base De Datos
Migraciones
Modelos Y Base De Datos
Operaciones Crud Y Consultas
Modelos Y Base De Datos
Enrutamiento Básico
Vistas Y Plantillas
Plantillas Con Django Template Language
Vistas Y Plantillas
Vistas Basadas En Funciones
Vistas Y Plantillas
Vistas Basadas En Clases
Vistas Y Plantillas
Middlewares
Vistas Y Plantillas
Form Vs Modelform
Formularios
Procesamiento De Formularios
Formularios
Subida De Archivos
Formularios
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender el uso del ORM de Django para operaciones CRUD.
- Crear nuevos registros y guardar datos en la base de datos.
- Leer y manejar excepciones en consultas.
- Actualizar objetos y utilizar operaciones aritméticas con
F()
. - Borrar registros y asegurar integridad y consistencia.
- Utilizar transacciones para asegurar atomicidad de las operaciones.