Django es un framwork web de alto nivel escrito en Python que fomenta el desarrollo rápido y el diseño limpio y pragmático. Creado por desarrolladores experimentados, se encarga de gran parte de las molestias del desarrollo web, permitiéndote concentrarte en escribir tu aplicación sin tener que reinventar la rueda. Es de código abierto y cuenta con una comunidad próspera y activa.
Arquitectura y Componentes Principales de un Proyecto Django
Un proyecto Django típicamente se estructura en varias aplicaciones reutilizables. Cada aplicación maneja una funcionalidad específica (como un blog, un sistema de autenticación o una tienda en línea). La estructura de directorios de un proyecto base incluye:
- Directorio del proyecto: Contiene la configuración global (
settings.py), la definición de URL raíz (urls.py) y el servidor WSGI (wsgi.py). - Archivos de gestión:
manage.pyes una utilidad de línea de comandos para interactuar con el proyecto (servidor de desarrollo, migraciones, etc.). - Aplicaciones: Cada aplicación tiene su propio directorio con archivos como:
models.py: Define la estructura de la base de datos usando el ORM de Django.views.py: Contiene la lógica de negocio para manejar peticiones HTTP.urls.py: Encamina URLs específicas de la aplicación a sus vistas correspondientes.
- Directorios de recursos:
templates/para archivos HTML con sintaxis de plantillas ystatic/para archivos estáticos (CSS, JavaScript, imágenes).
Configuración del Proyecto (settings.py)
Este archivo es el corazón de la configuración. A continuación, algunos ajustes clave:
Aplicaciones instaladas
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'mi_aplicacion.apps.MiAplicacionConfig', # Forma recomendada
# 'mi_aplicacion', # Forma abreviada
]
Directorios de plantillas
import os
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'plantillas')],
# ...
},
]
Acceso a archivos estáticos
Los archivos estáticos requieren configuración para ser servidos:
# Prefijo de URL para acceder a los archivos estáticos
STATIC_URL = '/archivos_estaticos/'
# Rutas de directorios donde se almacenan los archivos estáticos adicionales
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'recursos_comunes'),
]
En las plantillas, se cargan usando la etiqueta {% load static %}:
{% load static %}
<link rel="stylesheet" href="{% static 'css/estilos.css' %}">
Sistema de Enrutamiento (URL Dispatch)
Django utiliza una lista de patrones de URL definidos en urls.py para mapear solicitudes a funciones o clases de vista.
Definición básica de rutas
from django.urls import path
from . import vistas
urlpatterns = [
path('', vistas.pagina_inicio, name='inicio'),
path('articulos/<int:articulo_id>/', vistas.detalle_articulo, name='detalle_articulo'),
path('buscar/<str:termino>/', vistas.resultados_busqueda),
]
Convertidores de ruta
Permiten capturar valores de la URL y pasarlos a la vista. Ejemplos: <int:pk>, <str:nombre>, <slug:enlace>.
Resolución inversa y espacios de nombres
La resolución inversa genera URLs a partir de su nombre, evitando URLs codificadas:
# En el código Python (vista)
from django.urls import reverse
url = reverse('detalle_articulo', args=[42])
# En la plantilla HTML
<a href="{% url 'detalle_articulo' articulo_id=42 %}">Ver</a>
Para aplicaciones con URLs con el mismo nombre, se usan espacios de nombres:
# urls.py raíz
from django.urls import include, path
urlpatterns = [
path('noticias/', include(('noticias.urls', 'noticias'), namespace='noticias')),
]
# En la plantilla
<a href="{% url 'noticias:detalle' noticia.id %}">{# ... #}</a>
Capa de Vistas
Las vistas reciben una petición HTTP y devuelven una respuesta. Pueden ser funciones (FBV) o clases (CBV).
Funciones de Vista (FBV)
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, redirect
def mi_vista_funcional(request):
if request.method == 'POST':
# Procesar datos POST
nombre = request.POST.get('nombre')
return redirect('exito')
# Para GET u otros métodos
contexto = {'titulo': 'Mi Página'}
return render(request, 'mi_plantilla.html', contexto)
def api_endpoint(request):
datos = {'mensaje': 'OK', 'items': [1, 2, 3]}
return JsonResponse(datos)
Clases de Vista (CBV)
from django.views import View
from django.http import HttpResponse
class MiVistaClase(View):
def get(self, request, *args, **kwargs):
return HttpResponse("Respuesta a GET")
def post(self, request, *args, **kwargs):
return HttpResponse("Respuesta a POST")
# En urls.py
urlpatterns = [
path('clase/', MiVistaClase.as_view()),
]
El objeto Request
Proporciona información sobre la solicitud:
request.method: 'GET', 'POST', etc.request.GET: Datos de la consulta (query string).request.POST: Datos enviados via formulario POST.request.FILES: Archivos subidos.
Motor de Plantillas
Las plantillas HTML pueden incluir una sintaxis especial para lógica y variables.
Renderización y contexto
# En la vista
def index(request):
productos = Producto.objects.filter(disponible=True)
return render(request, 'tienda/index.html', {'productos': productos})
<!-- En index.html -->
{% for prod in productos %}
<div>{{ prod.nombre }} - {{ prod.precio|floatformat:2 }}</div>
{% empty %}
<p>No hay productos disponibles.</p>
{% endfor %}
Filtros
Transforman variables: {{ valor|filtrar:argumento }}. Ejemplos: date, default, truncatewords, length.
Herencia y bloques
<!-- base.html -->
<html>
<head>
<title>{% block titulo %}Sitio{% endblock %}</title>
</head>
<body>
{% block contenido %}{% endblock %}
</body>
</html>
<!-- pagina.html -->
{% extends "base.html" %}
{% block titulo %}Mi Página{% endblock %}
{% block contenido %}
<h1>Bienvenido</h1>
{% endblock %}
Sistema de Modelos (ORM)
El ORM mapea clases de Python a tablas de base de datos.
Definición de modelos
from django.db import models
class Departamento(models.Model):
nombre = models.CharField(max_length=100)
ubicacion = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.nombre
class Empleado(models.Model):
nombre_completo = models.CharField("Nombre", max_length=150)
departamento = models.ForeignKey(
Departamento,
on_delete=models.CASCADE,
related_name='empleados'
)
fecha_ingreso = models.DateField(auto_now_add=True)
salario = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
ordering = ['fecha_ingreso']
verbose_name_plural = 'Empleados'
Consultas y operaciones CRUD
# Crear
dep = Departamento.objects.create(nombre='Tecnología')
# Leer
todos = Empleado.objects.all()
techs = Empleado.objects.filter(departamento__nombre='Tecnología')
caros = Empleado.objects.filter(salario__gte=50000).order_by('-salario')
ultimo = Empleado.objects.latest('fecha_ingreso')
# Actualizar
Empleado.objects.filter(salario__lt=30000).update(salario=30000)
# Borrar
Empleado.objects.filter(activo=False).delete()
Relaciones
- ForeignKey (muchos a uno):
models.ForeignKey(OtroModelo, on_delete=models.CASCADE) - ManyToManyField:
models.ManyToManyField(OtroModelo) - OneToOneField:
models.OneToOneField(OtroModelo, on_delete=models.CASCADE)
Consultas relacionadas: Empleado.objects.select_related('departamento') (para FK) o Empleado.objects.prefetch_related('proyectos') (para M2M).
Consultas avanzadas (F y Q)
from django.db.models import F, Q
# Usar F() para referenciar campos en consultas
Empleado.objects.update(salario=F('salario') * 1.1)
# Usar Q() para consultas complejas
Empleado.objects.filter(
Q(salario__gt=40000) | Q(departamento__nombre='Ventas'),
activo=True
)
Agregación y Agrupación
from django.db.models import Avg, Count, Max
# Agregación sobre todo el queryset
Empleado.objects.aggregate(salario_promedio=Avg('salario'))
# Agrupación (GROUP BY)
EmpleadosPorDepto = Empleado.objects.values('departamento__nombre') \
.annotate(
num_empleados=Count('id'),
salario_max=Max('salario')
) \
.order_by('-num_empleados')
Migraciones
Sincronizan los cambios en los modelos con la base de datos:
python manage.py makemigrations: Genera los archivos de migración.python manage.py migrate: Aplica las migraciones a la base de datos.python manage.py showmigrations: Lista el estado de las migraciones.