Modelos y base de datos
En este capítulo aprenderás a definir modelos de datos con Django ORM, crear tablas de la base de datos e insertar datos de prueba.
¿Por qué se necesitan modelos?
En el capítulo anterior hardcodeamos el texto de las páginas. Un blog real necesita almacenar varios artículos y su contenido puede cambiar en cualquier momento.
Modelo (Model)Es la capa de base de datos de Django: defines la estructura de datos con clases de Python y Django genera automáticamente las tablas de base de datos correspondientes.
No necesitas escribir ni una línea de SQL: este esel valor fundamental delORM (mapeo objeto-relacional).
Definir el modelo Post
Abreblog/models.pyy define el modelo de artículo.
Ejemplo
from django.db import models
from django.utils import timezone
class Category(models.Model):
"""Categoría de artículo"""
name = models.CharField('Nombre de la categoría', max_length=50, unique=True)
# El slug es el identificador en inglés para la URL (p. ej. /category/django/)
slug = models.SlugField('Identificador de URL', max_length=50, unique=True)
class Meta:
verbose_name = 'Categoría'
verbose_name_plural = 'Categorías'
def __str__(self):
return self.name
class Post(models.Model):
"""Artículo de blog"""
# Título: CharField se usa para texto corto, max_length es obligatorio
title = models.CharField('Título', max_length=200)
# slug: identificador en inglés del artículo en la URL
slug = models.SlugField('Identificador de URL', max_length=200, unique=True)
# Resumen: TextField se usa para texto largo, blank=True indica que es opcional
summary = models.TextField('Resumen', blank=True)
# Contenido: TextField no tiene límite de longitud
content = models.TextField('Contenido')
# Categoría: ForeignKey es una relación uno a muchos, un artículo pertenece a una categoría
# on_delete=models.CASCADE indica que al eliminar una categoría, todos sus artículos también se eliminan
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
verbose_name='Categoría',
related_name='posts' # Consulta inversa: category.posts.all()
)
# auto_now_add: se rellena automáticamente con la hora actual al crearse
created_at = models.DateTimeField('Fecha de creación', auto_now_add=True)
# auto_now: se actualiza automáticamente a la hora actual en cada guardado
updated_at = models.DateTimeField('Fecha de actualización', auto_now=True)
class Meta:
verbose_name = 'Artículo'
verbose_name_plural = 'Artículos'
# Por defecto, se ordena por fecha de creación en orden descendente
ordering = ['-created_at']
def __str__(self):
# Al mostrar el objeto en el panel de Admin y en Shell, se muestra el título
return self.title
Referencia rápida de tipos de campo comunes
| Tipo de campo | Tipo de base de datos correspondiente | Caso de uso |
|---|---|---|
| CharField(max_length=N) | VARCHAR(N) | Texto corto: títulos, etiquetas, nombres, etc. |
| TextField() | TEXT | Contenido: cuerpo del artículo, resúmenes, comentarios, etc. |
| IntegerField() | INTEGER | Números enteros: recuentos, edades, etc. |
| DateTimeField() | DATETIME | Fecha de publicación, fecha de actualización |
| ForeignKey() | Restricción de clave foránea | Relación uno a muchos (artículo → categoría) |
| ManyToManyField() | Tabla intermedia | Relación muchos a muchos (artículo ↔ etiqueta) |
| SlugField() | VARCHAR | Identificador amigable para URL |
| ImageField() | VARCHAR(ruta) | Imagen de portada, avatar (requiere la biblioteca Pillow) |
__str__El método __str__ determina cómo se muestra el objeto como cadena de texto. En los menús desplegables del panel de Admin y en la salida de Shell, se invoca para mostrar el nombre del objeto. Si no escribes este método, verásPost object (1)texto sin sentido como este.
Migraciones: sincronizar los modelos con la base de datos
Una vez que hayas escrito las clases de modelo, necesitas usarmigraciones (Migration)para crear las tablas de la base de datos.
(venv) $ python manage.py makemigrations # 生成迁移文件(检测模型变化)
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Category
- Create model Post
(venv) $ python manage.py migrate # 执行迁移(创建数据库表)
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OK
La división de tareas de los dos comandos:
- makemigrationsmakemigrations: escanea models.py y genera el script de migración (archivo de Python)
- migratemigrate: ejecuta el script de migración y crea las tablas reales en la base de datos
Cada vez que modifiques models.py, debes ejecutar
makemigrations+migrateestos dos comandos. Ambos son imprescindibles: si solo ejecutas makemigrations sin migrate, las tablas no se crearán; si ejecutas migrate sin makemigrations, Django no sabrá que has modificado el modelo.
Insertar datos de prueba con Django Shell
Antes de empezar a escribir las páginas, introduce primero algunos datos en la base de datos con Django Shell para verificar que el modelo funciona correctamente.
(venv) $ python manage.py shell
Esto abre un shell interactivo de Python en el que puedes operar con la base de datos.
Ejemplo
from blog.models import Category, Post
# 1. Crear la categoría
cat_django = Category.objects.create(name='Django', slug='django')
cat_python = Category.objects.create(name='Python', slug='python')
cat_css = Category.objects.create(name='CSS', slug='css')
# 2. Crear los artículos
Post.objects.create(
title='Guía completa de introducción a Django',
slug='django-beginner-guide',
summary='Aprende Django desde cero, abarcando conceptos clave como modelos, vistas y plantillas.',
content='<h2>¿Por qué aprender Django?</h2><p>Django es uno de los frameworks web más populares de Python...</p>',
category=cat_django
)
Post.objects.create(
title='Programación asíncrona en Python en detalle',
slug='python-async',
summary='Entiende de una vez asyncio, las corrutinas y el bucle de eventos.',
content='<h2>¿Qué es una corrutina?</h2><p>Una corrutina es una solución de concurrencia más ligera que un hilo...</p>',
category=cat_python
)
Post.objects.create(
title='Práctica de diseño con CSS Grid',
slug='css-grid-layout',
summary='Implementa fácilmente diseños responsivos complejos con CSS Grid.',
content='<h2>Introducción a Grid</h2><p>Grid es un sistema de diseño bidimensional...</p>',
category=cat_css
)
# 3. Verificar las consultas
print(Post.objects.all()) # Mostrar todos los artículos
print(Post.objects.count()) # Número total de artículos
print(Post.objects.filter(category=cat_django)) # Artículos en la categoría Django
Consultas básicas con Django ORM
El ORM te permite operar con la base de datos mediante cadenas de métodos de Python, sin necesidad de escribir SQL.
| Método | SQL equivalente | Valor de retorno |
|---|---|---|
Post.objects.all() | SELECT * FROM blog_post | QuerySet (todos los artículos) |
Post.objects.get(id=1) | SELECT ... WHERE id=1 | Objeto individual (lanza una excepción si no se encuentra) |
Post.objects.filter(category__slug='django') | WHERE category.slug='django' | QuerySet |
Post.objects.order_by('-created_at') | ORDER BY created_at DESC | QuerySet |
Post.objects.exclude(status='draft') | WHERE status != 'draft' | QuerySet |
El QuerySet esde evaluación perezosa (lazy): al definir la consulta, no se ejecuta de inmediato; solo se envía el SQL cuando realmente se necesitan los datos (como al iterar, imprimir o convertir a list). Por lo tanto, puedes encadenar varios filter sin generar múltiples consultas.
Manos a la obra: prepara los datos para los siguientes capítulos
Introduce en el Shell más artículos y categorías, cubriendo las tres categorías Django, Python y CSS, con al menos 2 artículos en cada categoría.
Puedes escribir un comando de administración o un script para insertarlos en lote, pero por ahora es suficiente con insertarlos uno a uno en el Shell.
Resumen del capítulo
En este capítulo has dominado el flujo principal de Django ORM: definir clases de Python (modelos) en models.py, generar archivos de migración con makemigrations, sincronizar la base de datos con migrate, e insertar y consultar datos con Shell y ORM.
Con esto, la estructura de artículos y categorías ya está creada en la base de datos.
Otras extensiones