95

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, but the format isn't very machine readable.

For context: I have a script that shouldn't start running until the database has been migrated. For various reasons, it would be tricky to send a signal from the process that's running the migrations. So I'd like to have my script periodically check the database to see if all the migrations have run.

2
  • 1
    are you running automated script to check the migrations like fabric ? Commented Aug 5, 2015 at 19:25
  • 1
    stackoverflow.com/a/8491203/4325513 Commented Aug 5, 2015 at 19:31

11 Answers 11

106

Shell

The only simple solution I've found so far is running

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

which will output an empty string in case all migrations have been applied.

However, it is closely tied to the output format.

Python

I checked the source code of migrate command and it seems like this should do the trick:

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 not executor.migration_plan(targets) # Usage example. if is_database_synchronized(DEFAULT_DB_ALIAS): # All migrations have been applied. pass else: # Unapplied migrations found. pass 
Sign up to request clarification or add additional context in comments.

1 Comment

In Django 1.7 or up, you can use: ./manage showmigrations --list or ./manage showmigrations --plan
73

1.10 release notes:

The new makemigrations --check option makes the command exit with a non-zero status when model changes without migrations are detected.

If you don't want to create the migrations, combine it with --dry-run:

python manage.py makemigrations --check --dry-run 

Note that this doesn't check whether the migrations were applied, it only checks whether the migration files were created.

UPDATE for Django 4.2: --check will not create missing migration files and --dry-run is not required anymore.

UPDATE: the management command migrate also has a --check option:

Makes migrate exit with a non-zero status when unapplied migrations are detected.

8 Comments

While I consider this an answer to the more important question "How to know if I need to make migrations [and then apply those migrations]?" which the OP might well be after primarily or secondarily, it could easily be interpreted as not quite an answer to the specific question actually presented. I would say all the other answers have half of the process required, and your answer has the other half. I feel that technically the OP could only want one half, but realistically both halves should be involved in any truly useful answer for a question as vague as that presented.
The commands you list have exactly the same problem the OP had: the output is not easily machine readable. I don't see how you addressed that issue. The --check command gives a simple answer to the specific question the OP asked, "have all the migrations been run or not" and it's very well suited for automated tasks, that is exactly why it exists.
Mine was not added as a direct alternative to yours. I actually liked your answer better than the others. Just seemed incomplete. --check seems nice (for the small part of the process it is for), though it's annoying how the behavior has just changed from 1.8. I'll wait a little longer before using it. As for parsing output, it's obnoxious this process is complicated, but grep is simple & unavoidable in general. :p As to his exact question, I again consider the problem larger, without an "easy" solution. Will update an answer with script if I make one. Or someone else can.
I wish this was the correct answer, but it unfortunately does not work for migrations that have been created, but not applied.
Add the --dry-run option if you don't want to actually create the migrations.
|
23

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 

1 Comment

There is now a built-in for this: ./manage.py showmigrations accounts (no migrations) admin [X] 0001_initial [ ] 0002_logentry_remove_auto_add ....
19

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

Comments

13

./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

The documentation for these commands can be found here on the Django project's website.

3 Comments

To those who might attempt to edit this answer… The duplicate steps in this answer are not accidental, & you would know that if you took the time to understand the question or the answer. If you aren't checking yourself (as the duplicated steps in this answer would encourage), you will never master this process, & instead will almost certainly break something. If it was a non-complicated, completely safe process, the answer wouldn't be so very long as it is (even without duplicates). I do not foresee any terribly useful changes to be made to this answer without significant Django development.
Only thing I would add is the link to this section of the documentation
@KoltonNoreen Added a link to that page but not that specific section. Should do the trick I believe.
6

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..

Comments

5

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.

Comments

1

Tested for Django 3.2:

python manage.py makemigrations --check --dry-run 

expected output would be:

'No changes detected' 

if there are no pending changes in the Models requiring a migration to be created

python manage.py migrate --plan 

expected output would be:

'Planned operations: No planned migration operations.' 

You can use it in python script with call_command and develop a way to check for the expected output. If there are any pending makemigrations of migrate calls, the output will be different from the expected and you can understand that something is missing.

I'm running this at a CI/CD pipeline with very good results.

Comments

1

Here is another python solution, using a custom Django command :

from io import StringIO from time import sleep from django.core.management import BaseCommand, call_command class Command(BaseCommand): def handle(self, *, **__): pending_migrations = True while pending_migrations: output = StringIO() call_command("migrate", plan=True, stdout=output) command_result = output.getvalue() pending_migrations = not "No planned migration operations" in command_result if pending_migrations: sleep(5) 

It will retry every 5 seconds (I need it in my case but it's no mandatory to do so).

To use it, simply run python manage.py wait_for_migrations, assuming the code above is in a file wait_for_migrations.py, in a Django app, under <app>/management/commands (whole path: <app>/management/commands/wait_for_migrations.py.

To learn more about custom commands : LINK

I use this custom command in the startup sequence of my celery docker containers to be sure the database has been migrated by the Django backend container, before starting celery itself.

Comments

0

I checked it by looking up the table django_migrations, which stores all applied migrations.

1 Comment

I would advice to only check this table for information (changes to the table do not apply). I would stay with django commands like showmigrations, so you do not leave the database in an inconsistent state.
0

Make it simple:

$ until python manage.py migrate --check ; do echo "Migration not completed" ; done 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.