Tutorial de Django
Aprende Django desde cero: instala el framework, crea modelos, vistas, plantillas, ejecuta migraciones y lanza una app web funcional paso a paso.
Django es un framework web de alto nivel para Python que te permite crear aplicaciones web seguras y escalables de forma rápida. Sigue el patrón arquitectónico Model-Template-View (MTV) — un pariente cercano de MVC — e incluye un ORM, una interfaz de administración, enrutamiento de URL, autenticación y mucho más. La filosofía "batteries-included" de Django significa que puedes centrarte en la lógica de tu aplicación en lugar de ensamblar infraestructura.
Este tutorial recorre los pasos esenciales: instalar Django dentro de un entorno virtual, crear un proyecto y una app, definir modelos, escribir vistas, construir plantillas, ejecutar migraciones de base de datos y verificar todo en el navegador.
Por qué usar Django
Batteries Included
Django incluye un panel de administración integrado, autenticación de usuarios, manejo de formularios, un ORM, soporte de caché y utilidades de internacionalización. No es necesario buscar paquetes de terceros para cubrir estos aspectos básicos.
Seguridad por defecto
Django protege contra la inyección SQL usando consultas parametrizadas a través de su ORM. También ofrece protección integrada contra Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF) y clickjacking. La clave secreta, el modo DEBUG y la configuración ALLOWED_HOSTS reducen aún más la superficie de ataque.
Escalabilidad
Django impulsa sitios de alto tráfico como Instagram y Disqus. Su framework de caché se integra con Memcached y Redis, y el ORM soporta conexiones de base de datos en pool. Puedes escalar vertical u horizontalmente sin modificar el código de tu aplicación.
Gran ecosistema
El índice de paquetes de Django lista miles de apps de terceros — APIs REST (djangorestframework), manejo de imágenes, pasarelas de pago y más. La estabilidad del framework y su ciclo de versiones largo garantizan que estos paquetes se mantengan activamente.
Cómo funciona el patrón MTV de Django
| Capa | Término Django | Responsabilidad |
|---|---|---|
| Datos | Model | Clase Python que mapea a una tabla de base de datos |
| Presentación | Template | Archivo HTML con marcadores de posición {{ variable }} |
| Lógica | View | Función (o clase) Python que lee datos y devuelve una respuesta |
El despachador de URL (urls.py) mapea las rutas de las solicitudes entrantes a la vista correcta. La vista consulta los modelos y luego pasa los datos a una plantilla. El HTML renderizado se devuelve al navegador.
Configurar un entorno virtual
Siempre aísla los proyectos Django en un entorno virtual para que las dependencias no entren en conflicto entre proyectos.
# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # macOS / Linux
venv\Scripts\activate # WindowsTras la activación, el prompt muestra (venv). Cada pip install a partir de este punto afecta solo a este entorno.
Instalar Django
Con el entorno virtual activo, instala Django usando pip:
pip install djangoVerifica la instalación:
python -m django --versionDjango muestra una cadena de versión como 5.1.4.
Crear un proyecto Django
Un proyecto es el contenedor de nivel superior para todo tu sitio. Crea uno con:
django-admin startproject mysite
cd mysiteEsto genera la siguiente estructura:
mysite/
├── manage.py # Command-line utility for this project
└── mysite/
├── __init__.py
├── settings.py # Project configuration (database, installed apps, …)
├── urls.py # Root URL dispatcher
├── asgi.py # ASGI entry point
└── wsgi.py # WSGI entry pointmanage.py es tu herramienta principal para ejecutar comandos como iniciar el servidor de desarrollo, crear migraciones y crear un superusuario. La carpeta interna mysite/ es el paquete Python del propio proyecto.
Crear una app Django
Un proyecto puede contener múltiples apps — módulos autocontenidos, cada uno responsable de un área de funcionalidad. Crea una app de blog:
python manage.py startapp blogRegístrala para que Django pueda encontrar sus modelos, plantillas y archivos estáticos. Abre mysite/settings.py y añade 'blog' a INSTALLED_APPS:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # <-- add this line
]El directorio de la app tiene este aspecto después de la creación:
blog/
├── __init__.py
├── admin.py # Register models with the admin interface
├── apps.py # App configuration
├── migrations/ # Auto-generated migration files
│ └── __init__.py
├── models.py # Database models
├── tests.py
└── views.py # View functionsDefinir modelos
Un modelo es una clase Python que hereda de django.db.models.Model. Cada atributo de clase se corresponde con una columna de la base de datos. El ORM de Django traduce tu código Python a SQL y gestiona todas las interacciones con la base de datos.
Abre blog/models.py y define un modelo Post:
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Meta:
ordering = ['-date_posted'] # Newest posts firstTipos de campo clave usados aquí:
| Campo | Propósito |
|---|---|
CharField(max_length=N) | Texto corto con una longitud máxima |
TextField() | Texto de longitud ilimitada |
ForeignKey(…, on_delete=CASCADE) | Relación muchos-a-uno; elimina las publicaciones cuando se elimina su autor |
DateTimeField(auto_now_add=True) | Se establece una sola vez cuando se crea el registro |
DateTimeField(auto_now=True) | Se actualiza cada vez que se guarda el registro |
El método __str__ controla cómo se muestra el objeto en el panel de administración y en el shell de Python.
Ejecutar migraciones
Después de definir o modificar modelos debes crear y aplicar migraciones — instrucciones versionadas que actualizan el esquema de la base de datos.
# Generate a new migration file from your model changes
python manage.py makemigrations
# Apply all pending migrations to the database
python manage.py migrateVerás una salida como esta:
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Post
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OKEjecuta migrate cada vez que añadas, elimines o modifiques campos de un modelo. Nunca edites los archivos de migración a mano a menos que entiendas completamente su SQL.
Usar el panel de administración de Django
La interfaz de administración integrada te permite crear, leer, actualizar y eliminar registros desde el navegador — sin necesidad de una interfaz personalizada. Registra el modelo Post en blog/admin.py:
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'date_posted')
list_filter = ('date_posted', 'author')
search_fields = ('title', 'content')Crea una cuenta de superusuario para poder iniciar sesión:
python manage.py createsuperuserDjango solicita un nombre de usuario, correo electrónico y contraseña. Luego inicia el servidor de desarrollo y visita http://127.0.0.1:8000/admin/:
python manage.py runserverInicia sesión con las credenciales del superusuario y verás el modelo Posts listado en la sección "Blog".
Escribir vistas
Una vista es una función (o clase) Python que recibe una solicitud HTTP y devuelve una respuesta HTTP. Abre blog/views.py:
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
"""Display all published posts, newest first."""
posts = Post.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
"""Display a single post by primary key."""
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})get_object_or_404 es un atajo muy útil: obtiene el objeto de la base de datos o devuelve automáticamente una respuesta 404 si no existe. El tercer argumento de render es un diccionario de contexto — sus claves se convierten en variables de plantilla.
Crear plantillas
Las plantillas son archivos HTML que contienen etiquetas del Django Template Language (DTL). Crea la siguiente estructura de directorios:
blog/
└── templates/
└── blog/
├── base.html
├── post_list.html
└── post_detail.htmlblog/templates/blog/base.html — una maquetación compartida:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Blog{% endblock %}</title>
</head>
<body>
<header><h1>My Blog</h1></header>
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>
blog/templates/blog/post_list.html — la página de listado:
{% extends "blog/base.html" %}
{% block title %}All Posts{% endblock %}
{% block content %}
{% for post in posts %}
<article>
<h2><a href="/blog/{{ post.pk }}/">{{ post.title }}</a></h2>
<p>By {{ post.author }} on {{ post.date_posted|date:"N j, Y" }}</p>
<p>{{ post.content|truncatewords:30 }}</p>
</article>
{% empty %}
<p>No posts yet.</p>
{% endfor %}
{% endblock %}
blog/templates/blog/post_detail.html — la página de detalle:
{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h2>{{ post.title }}</h2>
<p>By {{ post.author }} on {{ post.date_posted|date:"N j, Y" }}</p>
<div>{{ post.content }}</div>
<a href="/">Back to all posts</a>
{% endblock %}
Características clave del DTL usadas arriba:
| Etiqueta / Filtro | Qué hace |
|---|---|
{% extends "…" %} | Hereda de una plantilla base |
{% block name %} | Define una sección reemplazable |
{{ variable }} | Muestra un valor (escapado en HTML automáticamente) |
{% for … %} / {% empty %} | Bucle con alternativa para listas vacías |
|date:"N j, Y" | Formatea un valor de fecha y hora |
|truncatewords:30 | Acorta el texto a 30 palabras |
Configurar URLs
La configuración de URL mapea las rutas de solicitudes a las funciones de vista.
blog/urls.py — crea este archivo en el directorio blog:
from django.urls import path
from . import views
app_name = 'blog' # Enables namespaced URL reversing
urlpatterns = [
path('', views.post_list, name='post_list'),
path('<int:pk>/', views.post_detail, name='post_detail'),
]mysite/urls.py — incluye las URLs de la app en la raíz del proyecto:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls', namespace='blog')),
path('', include('blog.urls', namespace='blog_root')),
]El convertidor de ruta <int:pk> captura un entero de la URL y lo pasa como argumento de palabra clave pk a post_detail.
Verificar el flujo completo
Inicia el servidor de desarrollo:
python manage.py runserverLuego abre http://127.0.0.1:8000/ en tu navegador. Si aún no existen publicaciones, la plantilla muestra "No posts yet." Inicia sesión en http://127.0.0.1:8000/admin/ y añade una publicación. Recarga la página de inicio — la publicación aparece inmediatamente.
El ciclo de vida de la solicitud para la página de inicio es:
- El navegador envía
GET / - El despachador de URL de Django coincide con
''y llama apost_list post_listconsultaPost.objects.all()(se traduce aSELECT * FROM blog_post ORDER BY date_posted DESC)- Django renderiza
post_list.htmlinsertando las publicaciones en la plantilla - El HTML renderizado se devuelve al navegador
Referencia rápida del ORM de Django
El ORM te permite construir consultas SQL usando cadenas de métodos Python. Conocer las búsquedas más comunes ahorra mucho tiempo:
# All posts
Post.objects.all()
# Posts by a specific author
Post.objects.filter(author__username='alice')
# Posts containing a word in the title (case-insensitive)
Post.objects.filter(title__icontains='django')
# The five most recent posts
Post.objects.order_by('-date_posted')[:5]
# Count posts
Post.objects.count()
# Get one object (raises DoesNotExist if not found)
Post.objects.get(pk=1)
# Exclude posts by a specific author
Post.objects.exclude(author__username='alice')Cada una de estas genera una consulta SQL solo cuando el queryset se evalúa (al iterarlo, cortarlo o convertirlo en una lista). Esto se llama evaluación perezosa y mantiene al mínimo las llamadas innecesarias a la base de datos.
Resumen de la estructura del proyecto
Tras completar este tutorial, la estructura del proyecto es la siguiente:
mysite/
├── manage.py
├── mysite/
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── blog/
├── admin.py
├── migrations/
│ └── 0001_initial.py
├── models.py
├── templates/
│ └── blog/
│ ├── base.html
│ ├── post_list.html
│ └── post_detail.html
├── urls.py
└── views.pyErrores comunes
Olvidar registrar la app. Si 'blog' no está en INSTALLED_APPS, Django no encontrará sus modelos ni plantillas y makemigrations no generará ninguna salida para esa app.
Omitir migrate tras makemigrations. Ejecutar solo makemigrations crea el archivo de migración pero no toca la base de datos. Siempre sigue con migrate.
Errores de plantilla no encontrada. Django busca plantillas dentro del directorio templates/ de cada app. La subcarpeta blog/ adicional dentro de templates/ (templates/blog/post_list.html) es una convención que evita colisiones de nombres entre apps — no la omitas.
DEBUG = True en producción. El servidor de desarrollo y DEBUG = True exponen trazas de error a cualquier persona. Establece DEBUG = False y configura ALLOWED_HOSTS antes de desplegar.
Próximos pasos
- Módulos de Python — comprende cómo el sistema de módulos de Python sustenta la estructura de importaciones de Django.
- Python PIP — gestiona Django y sus dependencias.
- Entornos virtuales de Python — mantén cada proyecto Django aislado.
- MySQL con Python — sustituye SQLite por una base de datos apta para producción.
- MongoDB con Python — usa un backend NoSQL con Django.
La documentación oficial de Django en docs.djangoproject.com cubre las vistas basadas en clases, formularios, autenticación, APIs REST con Django REST Framework y el despliegue en producción — todos ellos pasos naturales una vez que domines los fundamentos cubiertos aquí.