sql >> Base de Datos >  >> RDS >> Database

Migraciones de Django:una introducción

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

Nota: Especificando el nombre de la aplicación, 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 .

Nota: Puede notar que al ejecutar 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

Nota: El nombre del archivo de migración (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í:

  1. Crear o actualizar un modelo
  2. Ejecute ./manage.py makemigrations <app_name>
  3. Ejecute ./manage.py migrate para migrar todo o ./manage.py migrate <app_name> para migrar una aplicación individual
  4. 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!



Vídeo