In Django, is there an easy way to check whether all database migrations have been run? I\'ve found manage.py migrate --list
, which gives me the information I want,
Using @Ernest code, I've written a manage_custom.py for pending migrations. You can get the list of pending migrations also migrate those pending migrations (only), hence saving your time.
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 __name__ == '__main__':
args = sys.argv
migration = Migration()
migration.check_arguments(args)
migration.do_migration()
Usage:
# 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: Please setup environment as per your project structure.
3.1 release notes
The new migrate --check option makes the command exit with a non-zero status when unapplied migrations are detected.
https://docs.djangoproject.com/en/3.1/ref/django-admin/#cmdoption-migrate-check
so finally we can
python manage.py migrate --check
Try,
python manage.py migrate --list | grep "\[ \]\|^[a-z]" | grep "[ ]" -B 1
returns,
<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:
If you have updated Django version >= 1.11
, use below command,
python manage.py showmigrations | grep '\[ \]\|^[a-z]' | grep '[ ]' -B 1
Here is my Python soloution to get some information about the migration-states:
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
The result of this function looks like that:
[(True, 'contenttypes.0001_initial'),
(True, 'auth.0001_initial'),
(False, 'admin.0001_initial'),
(False, 'admin.0002_logentry_remove_auto_add')]
Maybe it helps some of you guys..
./manage.py showmigrations
#check which already-made migrations have been applied or not
(or: ./manage.py showmigrations someApp
#for specific app alone)
./manage.py makemigrations --dry-run
#check for migrations to be made
(or: ./manage.py makemigrations someApp --dry-run
#for specific app alone)
./manage.py makemigrations
#make the migrations
(or: ./manage.py makemigrations someApp
#for specific app alone)
./manage.py showmigrations
#check which already-made migrations have been applied or not
(or: ./manage.py showmigrations someApp
#for specific app alone)
./manage.py sqlmigrate someApp 0001
#view SQL changes for specific app & migration
./manage.py migrate
#apply migrations
(or: ./manage.py migrate someApp
#for specific app alone)
./manage.py showmigrations
#check which already-made migrations have been applied or not
(or: ./manage.py showmigrations someApp
#for specific app alone)
./manage.py makemigrations --dry-run
#check for migrations to be made
(or: ./manage.py makemigrations someApp --dry-run
#for specific app alone)
PS:
./manage.py migrate someApp zero
#unapply all migrations for specific app
I checked it by looking up the table django_migrations
, which stores all applied migrations.