Cuando presentamos el comando syncdb
previamente en este capítulo, hicimos
notar que syncdb
simplemente crea tablas que todavía no existen en tu base
de datos — no sincroniza cambios en modelos ni borra modelos. Si agregas o
modificas un campo de un modelo o si eliminas un modelo, será necesario que
realices el cambio en tu base de datos en forma manual. Esta sección explica
cómo hacerlo.
Cuando estás realizando cambios de esquema, es importante tener presente algunas características de la capa de base de datos de Django:
- Django se quejará estrepitosamente si un modelo contiene un campo que todavía no ha sido creado en la tabla de la base de datos. Esto causará un error la primera vez que uses la API de base de datos de Django para consultar la tabla en cuestión (esto es, ocurrirá en tiempo de ejecución y no en tiempo de compilación).
- A Django no le importa si una tabla de base de datos contiene columnas que no están definidas en el modelo.
- A Django no le importa si una base de datos contiene una tabla que no está representada por un modelo.
El realizar cambios al esquema de una base de datos es cuestión de cambiar las distintas piezas — el código Python y la base de datos en sí misma — en el orden correcto.
5.13.1. Agregar campos
Cuando se agrega un campo a una tabla/modelo en un entorno de producción, el truco es sacar ventaja del hecho que a Django no le importa si una tabla contiene columnas que no están definidas en el modelo. La estrategia es agregar la columna en la base de datos y luego actualizar el modelo Django para que incluya el nuevo campo.
Sin embargo, tenemos aquí un pequeño problema del huevo y la gallina, porque
para poder saber cómo debe expresarse la nueva columna en SQL, necesitas ver la
salida producida por el comando manage.py sqlall
de Django, el cual requiere
que el campo exista en el modelo. (Notar que no es un requerimiento el que
crees tu columna con exactamente el mismo SQL que usaría Django, pero es una
buena idea el hacerlo para estar seguros de que todo está en sincronía).
La solución al problema del huevo y la gallina es usar un entorno de desarrollo en lugar de realizar los cambios en un servidor de producción. (Estás usando un entorno de pruebas/desarrollo, ¿no es cierto?). Este es el detalle de los pasos a seguir.
Primero, realiza los siguientes pasos en el entorno de desarrollo (o sea, no en el servidor de producción):
- Agrega el campo a tu modelo.
- Ejecuta
manage.py sqlall [yourapp]
para ver la nueva sentenciaCREATE TABLE
para el modelo. Toma nota de la definición de la columna para el nuevo campo. - Arranca el shell interactivo de tu base de datos (por ej.
psql
omysql
, o puedes usarmanage.py dbshell
). Ejecuta una sentenciaALTER TABLE
que agregue tu nueva columna. - (Opcional) Arranca el shell interactivo de Python con
manage.py shell
y verifica que el nuevo campo haya sido agregado correctamente importando el modelo y seleccionando desde la tabla (por ej.MyModel.objects.all()[:5]
).
Entonces en el servidor de producción realiza los siguientes pasos:
- Arranca el shell interactivo de tu base de datos.
- Ejecuta la sentencia
ALTER TABLE
que usaste en el paso 3 de arriba. - Agrega el campo a tu modelo. Si estás usando un sistema de control de
revisiones de código fuente y has realizado un check in de la
modificación del paso 1 del trabajo en el entorno de desarrollo, entonces
puedes actualizar el código (por ej.
svn update
si usas Subversion) en el servidor de producción. - Reinicia el servidor Web para que los cambios en el código surtan efecto.
Por ejemplo, hagamos un repaso de los que haríamos si agregáramos un campo
num_pages
al modelo Book
descrito previamente en este capítulo. Primero,
alteraríamos el modelo en nuestro entorno de desarrollo para que se viera así:
class Book(models.Model):
title = models.CharField(maxlength=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.title
(Nota: Revisa el apartado "Agregando columnas NOT NULL" para conocer detalles
importantes acerca de por qué hemos incluido blank=True
y null=True
).
Luego ejecutaríamos el comando manage.py sqlall books
para ver la sentencia
CREATE TABLE
. La misma se vería similar a esto:
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),
"publication_date" date NOT NULL,
"num_pages" integer NULL
);
La nueva columna está representada de la siguiente manera:
"num_pages" integer NULL
A continuación, arrancaríamos el shell interactivo de base de datos en nuestra
base de datos de desarrollo escribiendo psql
(para PostgreSQL), y
ejecutaríamos la siguiente sentencia:
ALTER TABLE books_book ADD COLUMN num_pages integer;
Luego de la sentencia ALTER TABLE
, verificaríamos que el cambio haya
funcionado correctamente, para ello iniciaríamos el shell de Python y
ejecutaríamos este código:
>>> from mysite.books.models import Book
>>> Book.objects.all()[:5]
Si dicho código no produjera errores, podríamos movernos a nuestro servidor de
producción y ejecutaríamos la sentencia ALTER TABLE
en la base de datos de
producción. Entonces, actualizaríamos el modelo en el entorno de producción y
reiniciaríamos el servidor Web.
5.13.2. Eliminar campos
Eliminar un campo de un modelo es mucho más fácil que agregar uno. Para borrar un campo, sólo sigue los siguientes pasos:
- Elimina el campo de tu modelo y reinicia el servidor Web.
- Elimina la columna de tu base de datos, usando un comando como este:
ALTER TABLE books_book DROP COLUMN num_pages;
5.13.3. Eliminar campos Many-to-Many
Debido a que los campos many-to-many son diferentes a los campos normales, el proceso de borrado es diferente:
- Elimina el campo
ManyToManyField
de tu modelo y reinicia el servidor Web. - Elimina la tabla many-to-many de tu base de datos, usando un comando como este:
DROP TABLE books_books_publishers;
5.13.4. Eliminar modelos
Eliminar completamente un modelo es tan fácil como el eliminar un campo. Para borrar un modelo, sigue los siguientes pasos:
- Elimina el modelo de tu archivo
models.py
y reinicia el servidor Web. - Elimina la tabla de tu base de datos, usando un comando como este:
DROP TABLE books_book;