web-dev-qa-db-de.com

Überprüfen Sie auf ausstehende Django-Migrationen

Gibt es in Django eine einfache Möglichkeit zu überprüfen, ob alle Datenbankmigrationen ausgeführt wurden? Ich habe manage.py migrate --list gefunden, was mir die gewünschten Informationen gibt, aber das Format ist nicht sehr maschinenlesbar.

Für Kontext: Ich habe ein Skript, das erst nach der Migration der Datenbank ausgeführt werden sollte. Aus verschiedenen Gründen ist es schwierig, ein Signal von dem Prozess zu senden, der die Migrationen durchführt. Daher möchte ich, dass mein Skript die Datenbank regelmäßig überprüft, um festzustellen, ob alle Migrationen ausgeführt wurden.

21
Moss Collum

Shell

Die einzige einfache Lösung, die ich bisher gefunden habe, ist das Laufen

./manage.py showmigrations | grep '\[ \]'

in diesem Fall wird eine leere Zeichenfolge ausgegeben, wenn alle Migrationen angewendet wurden.

Es ist jedoch eng an das Ausgabeformat gebunden.

Python

Ich habe den Quellcode des Befehls migrate überprüft und es scheint, als ob dies den Trick tun sollte:

from Django.db.migrations.executor import MigrationExecutor
from Django.db import connections, DEFAULT_DB_ALIAS


def is_database_synchronized(database):
    connection = connections[database]
    connection.prepare_database()
    executor = MigrationExecutor(connection)
    targets = executor.loader.graph.leaf_nodes()
    return False if executor.migration_plan(targets) else True

# Usage example.
if is_database_synchronized(DEFAULT_DB_ALIAS):
    # All migrations have been applied.
    pass
else:
    # Unapplied migrations found.
    pass
35
Ernest Ten

1.10 Versionshinweise:

Die neue Option makemigrations --check bewirkt, dass der Befehl mit einem Status ungleich Null beendet wird, wenn Modelländerungen ohne Migration erkannt werden.

11
minusf

Versuchen,

python manage.py migrate --list | grep "\[ \]\|^[a-z]" | grep "[ ]" -B 1

kehrt zurück,

<app_1>
 [ ] 0001_initial
 [ ] 0002_auto_01201244
 [ ] 0003_auto_12334333

<app_2>
 [ ] 0031_auto_12344544
 [ ] 0032_auto_45456767
 [ ] 0033_auto_23346566

<app_3>
 [ ] 0008_auto_3446677


Update :

Wenn Sie die Django-Version> = 1.11 aktualisiert haben, verwenden Sie den folgenden Befehl

python manage.py showmigrations | grep '\[ \]\|^[a-z]' | grep '[  ]' -B 1

./manage.py showmigrations #check welche bereits durchgeführten Migrationen angewendet wurden oder nicht
(oder: ./manage.py showmigrations someApp # für eine bestimmte Anwendung allein)

./manage.py makemigrations --dry-run #check für Migrationen
(oder: ./manage.py makemigrations someApp --dry-run # für eine bestimmte Anwendung allein)

./manage.py makemigrations #die Migrationen durchführen
(oder: ./manage.py makemigrations someApp # für eine bestimmte Anwendung allein)

./manage.py showmigrations #check welche bereits durchgeführten Migrationen angewendet wurden oder nicht
(oder: ./manage.py showmigrations someApp # für eine bestimmte Anwendung allein)

./manage.py sqlmigrate someApp 0001 #view SQL-Änderungen für bestimmte Anwendungen und Migrationen

./manage.py migrate #apply Migrationen
(oder: ./manage.py migrate someApp # für eine bestimmte Anwendung allein)

./manage.py showmigrations #check welche bereits durchgeführten Migrationen angewendet wurden oder nicht
(oder: ./manage.py showmigrations someApp # für eine bestimmte Anwendung allein)

./manage.py makemigrations --dry-run #check für Migrationen
(oder: ./manage.py makemigrations someApp --dry-run # für eine bestimmte Anwendung allein)

PS:
./manage.py migrate someApp zero #unapply alle Migrationen für eine bestimmte App

4
Jan Kyu Peblik

Mit @Ernest-Code habe ich einen manage_custom.py für ausstehende Migrationen geschrieben. Sie können die Liste ausstehender Migrationen auch Migrieren dieser ausstehenden Migrationen (nur) erhalten, wodurch Sie Zeit sparen.

manage_custom.py

__author__ = "Parag Tyagi"

# set environment
import os
import sys
import Django
sys.path.append('../')
os.environ.setdefault('Django_SETTINGS_MODULE', 'settings')
Django.setup()

from Django.core.management import execute_from_command_line
from Django.db import DEFAULT_DB_ALIAS, connections
from Django.db.migrations.executor import MigrationExecutor


class Migration(object):
    """
    A custom manage.py file for managing pending migrations (only)
    """

    def __init__(self, migrate_per_migration_id=False):
        """
        :param migrate_per_migration_id: Setting this to `True` will migrate each pending migration of any
        particular app individually. `False` will migrate the whole app at a time.

        You can add more arguments (viz. showmigrations, migrate) by defining the argument with prefix as 'ARGV_'
        and create its functionality accordingly.
        """
        self.ARG_PREFIX = 'ARGV_'
        self.MIGRATE_PER_MIGRATION_ID = migrate_per_migration_id
        self.ARGV_showmigrations = False
        self.ARGV_migrate = False

    @staticmethod
    def get_pending_migrations(database):
        """
        :param database: Database alias
        :return: List of pending migrations
        """
        connection = connections[database]
        connection.prepare_database()
        executor = MigrationExecutor(connection)
        targets = executor.loader.graph.leaf_nodes()
        return executor.migration_plan(targets)

    def check_arguments(self, args):
        """
        Method for checking arguments passed while running the command
        :param args: Dictionary of arguments passed while running the script file
        :return: Set the argument variable ('ARGV_<argument>') to True if found else terminate the script
        """
        required_args = filter(None, [var.split(self.ARG_PREFIX)[1] if var.startswith(self.ARG_PREFIX)
                                      else None for var in self.__dict__.keys()])
        if any(k in args for k in required_args):
            for arg in required_args:
                if arg in args:
                    setattr(self, '{}{}'.format(self.ARG_PREFIX, arg), True)
                    break
        else:
            print ("Please pass argument: {}"
                   "\ne.g. python manage_custom.py {}".format(required_args, required_args[0]))
            sys.exit()

    def do_migration(self):
        """
        Migrates all the pending migrations (if any)
        """
        pending_migrations = self.get_pending_migrations(DEFAULT_DB_ALIAS)
        if pending_migrations:
            done_app = []
            for mig in pending_migrations:
                app, migration_id = str(mig[0]).split('.')
                commands = ['manage.py', 'migrate'] + ([app, migration_id] if self.MIGRATE_PER_MIGRATION_ID else [app])
                if self.ARGV_migrate and (app not in done_app or self.MIGRATE_PER_MIGRATION_ID):
                    execute_from_command_line(commands)
                    done_app.append(app)
                Elif self.ARGV_showmigrations:
                    print (str(mig[0]))
        else:
            print ("No pending migrations")


if __== '__main__':
    args = sys.argv
    migration = Migration()
    migration.check_arguments(args)
    migration.do_migration()

Verwendungszweck:

# below command will show all pending migrations
python manage_custom.py showmigrations

# below command will migrate all pending migrations
python manage_custom.py migrate

PS: Bitte richten Sie die Umgebung entsprechend Ihrer Projektstruktur ein.

ich überprüfe es durch einen Blick auf die Datenbank in der Tabelle Django_migrations, in der alle angewendeten Migrationen gespeichert werden 

Hier ist meine Python-Lösung, um Informationen zu den Migrationszuständen zu erhalten:

from io import StringIO  # for Python 2 use from StringIO import StringIO  
from Django.core.management import call_command 

def get_migration_state():
    result = []
    out = StringIO()
    call_command('showmigrations', format="plan", stdout=out)
    out.seek(0)
    for line in out.readlines():
        status, name = line.rsplit(' ', 1)
        result.append((status.strip() == '[X]', name.strip()))
    return result

Das Ergebnis dieser Funktion sieht so aus:

[(True, 'contenttypes.0001_initial'),
 (True, 'auth.0001_initial'),
 (False, 'admin.0001_initial'),
 (False, 'admin.0002_logentry_remove_auto_add')]

Vielleicht hilft es einigen von euch ...

0
chsymann