Ver ahora Este tutorial tiene un curso de video relacionado creado por el equipo de Real Python. Mírelo junto con el tutorial escrito para profundizar su comprensión:Django Migrations 101
Desde la versión 1.7, Django viene con soporte incorporado para migraciones de bases de datos. En Django, las migraciones de bases de datos suelen ir de la mano con los modelos:cada vez que codifica un nuevo modelo, también genera una migración para crear la tabla necesaria en la base de datos. Sin embargo, las migraciones pueden hacer mucho más.
Aprenderá cómo funcionan las migraciones de Django y cómo puede aprovecharlas al máximo en el transcurso de cuatro artículos y un video:
- Parte 1:Migraciones de Django:Introducción (artículo actual)
- Parte 2:profundizar en las migraciones
- Parte 3:Migraciones de datos
- Video:Migraciones de Django 1.7:introducción
En este artículo, se familiarizará con las migraciones de Django y aprenderá lo siguiente:
- Cómo crear tablas de bases de datos sin escribir SQL
- Cómo modificar automáticamente su base de datos después de cambiar sus modelos
- Cómo revertir los cambios realizados en su base de datos
Bono Gratis: Haga clic aquí para obtener acceso a una Guía de recursos de aprendizaje de Django (PDF) gratuita que le muestra consejos y trucos, así como las trampas comunes que debe evitar al crear aplicaciones web de Python + Django.
Los problemas que resuelven las migraciones
Si es nuevo en Django o en el desarrollo web en general, es posible que no esté familiarizado con el concepto de migraciones de bases de datos y puede que no parezca obvio por qué son una buena idea.
Primero, definamos rápidamente un par de términos para asegurarnos de que todos estén en la misma página. Django está diseñado para trabajar con una base de datos relacional, almacenada en un sistema de administración de base de datos relacional como PostgreSQL, MySQL o SQLite.
En una base de datos relacional, los datos se organizan en tablas. Una tabla de base de datos tiene un cierto número de columnas, pero puede tener cualquier número de filas. Cada columna tiene un tipo de datos específico, como una cadena de cierta longitud máxima o un entero positivo. La descripción de todas las tablas con sus columnas y sus respectivos tipos de datos se denomina esquema de base de datos.
Todos los sistemas de bases de datos compatibles con Django utilizan el lenguaje SQL para crear, leer, actualizar y eliminar datos en una base de datos relacional. SQL también se usa para crear, cambiar y eliminar las propias tablas de la base de datos.
Trabajar directamente con SQL puede ser bastante engorroso, por lo que para hacer su vida más fácil, Django viene con un mapeador relacional de objetos u ORM para abreviar. El ORM asigna la base de datos relacional al mundo de la programación orientada a objetos. En lugar de definir tablas de base de datos en SQL, escribe modelos de Django en Python. Sus modelos definen campos de base de datos, que corresponden a las columnas en sus tablas de base de datos.
Este es un ejemplo de cómo se asigna una clase de modelo de Django a una tabla de base de datos:
Pero simplemente definir una clase de modelo en un archivo de Python no hace que una tabla de base de datos aparezca mágicamente de la nada. Crear las tablas de la base de datos para almacenar sus modelos Django es el trabajo de una migración de base de datos. Además, siempre que realice un cambio en sus modelos, como agregar un campo, la base de datos también debe cambiarse. Las migraciones también manejan eso.
Aquí hay algunas formas en que las migraciones de Django te hacen la vida más fácil.
Realizar cambios en la base de datos sin SQL
Sin migraciones, tendría que conectarse a su base de datos y escribir un montón de comandos SQL o usar una herramienta gráfica como PHPMyAdmin para modificar el esquema de la base de datos cada vez que quisiera cambiar la definición de su modelo.
En Django, las migraciones se escriben principalmente en Python, por lo que no tiene que saber nada de SQL a menos que tenga casos de uso realmente avanzados.
Evitar la repetición
Crear un modelo y luego escribir SQL para crear las tablas de la base de datos sería repetitivo.
Las migraciones se generan a partir de sus modelos, asegurándose de que no se repita.
Garantizar las definiciones del modelo y el esquema de la base de datos sincronizados
Por lo general, tiene varias instancias de su base de datos, por ejemplo, una base de datos para cada desarrollador de su equipo, una base de datos para pruebas y una base de datos con datos en vivo.
Sin migraciones, deberá realizar cualquier cambio de esquema en cada una de sus bases de datos y deberá realizar un seguimiento de los cambios que ya se han realizado en cada base de datos.
Con las migraciones de Django, puede sincronizar fácilmente varias bases de datos con sus modelos.
Seguimiento de cambios en el esquema de la base de datos en el control de versiones
Un sistema de control de versiones, como Git, es excelente para el código, pero no tanto para los esquemas de bases de datos.
Como las migraciones son simplemente Python en Django, puede ponerlas en un sistema de control de versiones como cualquier otra pieza de código.
A estas alturas, es de esperar que esté convencido de que las migraciones son una herramienta útil y poderosa. Comencemos a aprender cómo liberar ese poder.
Configuración de un proyecto Django
A lo largo de este tutorial, trabajará en una aplicación simple de seguimiento de Bitcoin como proyecto de ejemplo.
El primer paso es instalar Django. Así es como lo hace en Linux o macOS X usando un entorno virtual:
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install "Django==2.1.*"
...
Successfully installed Django-2.1.3
Ahora ha creado un nuevo entorno virtual y lo ha activado, además de haber instalado Django en ese entorno virtual.
Tenga en cuenta que en Windows, ejecutaría env/bin/activate.bat
en lugar de source env/bin/activate
para activar su entorno virtual.
Para facilitar la lectura, los ejemplos de la consola no incluirán el (env)
parte del mensaje a partir de ahora.
Con Django instalado, puede crear el proyecto usando los siguientes comandos:
$ django-admin.py startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_data
Esto le brinda un proyecto simple y una aplicación llamada historical_data
. Ahora debería tener esta estructura de directorios:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ │ └── __init__.py
| |
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
└── manage.py
Dentro del bitcoin_tracker
directorio, hay dos subdirectorios:bitcoin_tracker
para archivos de todo el proyecto y historical_data
que contiene archivos para la aplicación que creó.
Ahora, para crear un modelo, agregue esta clase en historical_data/models.py
:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.PositiveIntegerField()
Este es el modelo básico para realizar un seguimiento de los precios de Bitcoin.
Además, no olvide agregar la aplicación recién creada a settings.INSTALLED_APPS
. Abra bitcoin_tracker/settings.py
y agregue historical_data
a la lista INSTALLED_APPS
, así:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'historical_data',
]
Las otras configuraciones están bien para este proyecto. Este tutorial asume que su proyecto está configurado para usar una base de datos SQLite, que es la predeterminada.
Creación de migraciones
Con el modelo creado, lo primero que debe hacer es crear una migración para él. Puedes hacer esto con el siguiente comando:
$ python manage.py makemigrations historical_data
Migrations for 'historical_data':
historical_data/migrations/0001_initial.py
- Create model PriceHistory
historical_data
, es opcional. Dejarlo crea migraciones para todas las aplicaciones.
Esto crea el archivo de migraciones que le indica a Django cómo crear las tablas de la base de datos para los modelos definidos en su aplicación. Echemos otro vistazo al árbol de directorios:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── migrations/
│ │ ├── 0001_initial.py
│ │ └── __init__.py
| |
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
├── db.sqlite3
└── manage.py
Como puede ver, las migrations
El directorio ahora contiene un nuevo archivo:0001_initial.py
.
makemigrations
El comando también creó el archivo db.sqlite3
, que contiene su base de datos SQLite.
Cuando intente acceder a un archivo de base de datos SQLite3 no existente, se creará automáticamente.
Este comportamiento es exclusivo de SQLite3. Si usa cualquier otro backend de base de datos como PostgreSQL o MySQL, debe crear la base de datos usted mismo antes ejecutando makemigrations
.
Puede echar un vistazo a la base de datos con dbshell
comando de gestión. En SQLite, el comando para listar todas las tablas es simplemente .tables
:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
sqlite>
La base de datos sigue vacía. Eso cambiará cuando aplique la migración. Escribe .quit
para salir del shell de SQLite.
Aplicando Migraciones
Ahora ha creado la migración, pero para realizar cambios en la base de datos, debe aplicarla con el comando de administración migrate
:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying historical_data.0001_initial... OK
Applying sessions.0001_initial... OK
¡Muchas cosas están pasando aquí! Según el resultado, su migración se ha aplicado correctamente. Pero, ¿de dónde vienen todas las demás migraciones?
Recuerda la configuración INSTALLED_APPS
? Algunas de las otras aplicaciones enumeradas allí también vienen con migraciones, y migrate
El comando de administración aplica las migraciones para todas las aplicaciones instaladas de forma predeterminada.
Eche otro vistazo a la base de datos:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups historical_data_pricehistory
auth_user_user_permissions
sqlite>
Ahora hay varias tablas. Sus nombres te dan una idea de su propósito. La migración que generó en el paso anterior ha creado el historical_data_pricehistory
mesa. Inspeccionémoslo usando .schema
comando:
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory"(
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
El .schema
el comando imprime el CREATE
instrucción que ejecutaría para crear la tabla. El parámetro --indent
lo formatea muy bien. Incluso si no está familiarizado con la sintaxis SQL, puede ver que el esquema del historical_data_pricehistory
la tabla refleja los campos del PriceHistory
modelo.
Hay una columna para cada campo y una columna adicional id
para la clave principal, que Django crea automáticamente a menos que especifique explícitamente una clave principal en su modelo.
Esto es lo que sucede si ejecuta migrate
comando de nuevo:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
¡Ninguna cosa! Django recuerda qué migraciones ya se han aplicado y no intenta volver a ejecutarlas.
Vale la pena señalar que también puede limitar el migrate
comando de gestión a una sola aplicación:
$ python manage.py migrate historical_data
Operations to perform:
Apply all migrations: historical_data
Running migrations:
No migrations to apply.
Como puede ver, Django ahora solo aplica migraciones para los historical_data
aplicación.
Cuando ejecuta las migraciones por primera vez, es una buena idea aplicar todas las migraciones para asegurarse de que su base de datos contenga las tablas necesarias para las funciones que puede dar por sentado, como la autenticación de usuario y las sesiones.
Cambio de modelos
Tus modelos no están grabados en piedra. Sus modelos cambiarán a medida que su proyecto Django obtenga más funciones. Puede agregar o eliminar campos o cambiar sus tipos y opciones.
Cuando cambia la definición de un modelo, las tablas de la base de datos utilizadas para almacenar estos modelos también deben cambiarse. Si las definiciones de su modelo no coinciden con el esquema de su base de datos actual, lo más probable es que se encuentre con un django.db.utils.OperationalError
.
Entonces, ¿cómo cambias las tablas de la base de datos? Creando y aplicando una migración.
Mientras prueba su rastreador de Bitcoin, se da cuenta de que cometió un error. La gente está vendiendo fracciones de Bitcoin, por lo que el campo volume
debe ser del tipo DecimalField
en lugar de PositiveIntegerField
.
Cambiemos el modelo para que se vea así:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.DecimalField(max_digits=7, decimal_places=3)
Sin migraciones, tendría que averiguar la sintaxis de SQL para convertir un PositiveIntegerField
en un DecimalField
. Afortunadamente, Django se encargará de eso por ti. Solo dile que haga migraciones:
$ python manage.py makemigrations
Migrations for 'historical_data':
historical_data/migrations/0002_auto_20181112_1950.py
- Alter field volume on pricehistory
0002_auto_20181112_1950.py
) se basa en la hora actual y será diferente si lo sigue en su sistema.
Ahora aplica esta migración a su base de datos:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying historical_data.0002_auto_20181112_1950... OK
La migración se aplicó con éxito, por lo que puede usar dbshell
para verificar que los cambios surtieron efecto:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" decimal NOT NULL
);
Si compara el nuevo esquema con el esquema que vio anteriormente, notará que el tipo de volume
la columna ha cambiado de integer
a decimal
para reflejar el cambio del volume
campo en el modelo de PositiveIntegerField
a DecimalField
.
Enumeración de migraciones
Si desea saber qué migraciones existen en un proyecto de Django, no tiene que buscar en las migrations
directorios de sus aplicaciones instaladas. Puede usar showmigrations
comando:
$ ./manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
historical_data
[X] 0001_initial
[X] 0002_auto_20181112_1950
sessions
[X] 0001_initial
Esto enumera todas las aplicaciones en el proyecto y las migraciones asociadas con cada aplicación. Además, pondrá una gran X
junto a las migraciones que ya se han aplicado.
Para nuestro pequeño ejemplo, showmigrations
El comando no es particularmente emocionante, pero es útil cuando comienza a trabajar en una base de código existente o trabaja en un equipo donde no es la única persona que agrega migraciones.
Desaplicar Migraciones
Ahora sabe cómo realizar cambios en el esquema de su base de datos creando y aplicando migraciones. En algún momento, es posible que desee deshacer los cambios y volver a un esquema de base de datos anterior porque:
- Quiero probar una migración que escribió un colega
- Date cuenta de que un cambio que hiciste fue una mala idea
- Trabaje en varias funciones con diferentes cambios en la base de datos en paralelo
- Desea restaurar una copia de seguridad que se creó cuando la base de datos aún tenía un esquema anterior
Afortunadamente, las migraciones no tienen que ser una calle de sentido único. En muchos casos, los efectos de una migración se pueden deshacer si no se aplica una migración. Para cancelar la aplicación de una migración, debe llamar a migrate
con el nombre de la aplicación y el nombre de la migración antes la migración que desea cancelar.
Si desea revertir la migración 0002_auto_20181112_1950
en tus historical_data
app, tienes que pasar 0001_initial
como argumento para migrate
comando:
$ python manage.py migrate historical_data 0001_initial
Operations to perform:
Target specific migration: 0001_initial, from historical_data
Running migrations:
Rendering model states... DONE
Unapplying historical_data.0002_auto_20181112_1950... OK
La migración no se aplicó, lo que significa que los cambios en la base de datos se revirtieron.
Dejar de aplicar una migración no elimina su archivo de migración. La próxima vez que ejecute migrate
comando, la migración se aplicará de nuevo.
Precaución: No confunda la anulación de la aplicación de migraciones con la operación de deshacer a la que está acostumbrado desde su editor de texto favorito.
No todas las operaciones de la base de datos se pueden revertir por completo. Si elimina un campo de un modelo, crea una migración y la aplica, Django eliminará la columna respectiva de la base de datos.
Si no se aplica esa migración, se volverá a crear la columna, ¡pero no recuperará los datos almacenados en esa columna!
Cuando se trata de nombres de migración, Django le ahorra algunas pulsaciones de teclas al no obligarlo a deletrear el nombre completo de la migración. Solo necesita lo suficiente del nombre para identificarlo de forma única.
En el ejemplo anterior, hubiera bastado con ejecutar python manage.py migrate historical_data 0001
.
Nombrar migraciones
En el ejemplo anterior, a Django se le ocurrió un nombre para la migración basado en la marca de tiempo, algo así como *0002_auto_20181112_1950
. Si no está satisfecho con eso, entonces puede usar --name
parámetro para proporcionar un nombre personalizado (sin el .py
extensión).
Para probar eso, primero debe eliminar la migración anterior. Ya lo ha desactivado, por lo que puede eliminar el archivo de forma segura:
$ rm historical_data/migrations/0002_auto_20181112_1950.py
Ahora puedes recrearlo con un nombre más descriptivo:
$ ./manage.py makemigrations historical_data --name switch_to_decimals
Esto creará la misma migración que antes excepto con el nuevo nombre de 0002_switch_to_decimals
.
Conclusión
Cubriste bastante terreno en este tutorial y aprendiste los fundamentos de las migraciones de Django.
En resumen, los pasos básicos para usar las migraciones de Django se ven así:
- Crear o actualizar un modelo
- Ejecute
./manage.py makemigrations <app_name>
- Ejecute
./manage.py migrate
para migrar todo o./manage.py migrate <app_name>
para migrar una aplicación individual - Repita según sea necesario
¡Eso es todo! Este flujo de trabajo funcionará la mayor parte del tiempo, pero si las cosas no funcionan como se esperaba, también sabrá cómo enumerar y cancelar las migraciones.
Si previamente creó y modificó las tablas de su base de datos con SQL escrito a mano, ahora se ha vuelto mucho más eficiente al delegar este trabajo a las migraciones de Django.
En el siguiente tutorial de esta serie, profundizará en el tema y aprenderá cómo funcionan las migraciones de Django bajo el capó.
Bono Gratis: Haga clic aquí para obtener acceso a una Guía de recursos de aprendizaje de Django (PDF) gratuita que le muestra consejos y trucos, así como las trampas comunes que debe evitar al crear aplicaciones web de Python + Django.
¡Salud!