Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

showmigrations.py 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from django.core.management.base import BaseCommand, CommandError
  2. from django.db import DEFAULT_DB_ALIAS, connections
  3. from django.db.migrations.loader import MigrationLoader
  4. class Command(BaseCommand):
  5. help = "Shows all available migrations for the current project"
  6. def add_arguments(self, parser):
  7. parser.add_argument(
  8. 'app_label', nargs='*',
  9. help='App labels of applications to limit the output to.',
  10. )
  11. parser.add_argument(
  12. '--database', action='store', dest='database', default=DEFAULT_DB_ALIAS,
  13. help='Nominates a database to synchronize. Defaults to the "default" database.',
  14. )
  15. formats = parser.add_mutually_exclusive_group()
  16. formats.add_argument(
  17. '--list', '-l', action='store_const', dest='format', const='list',
  18. help='Shows a list of all migrations and which are applied.',
  19. )
  20. formats.add_argument(
  21. '--plan', '-p', action='store_const', dest='format', const='plan',
  22. help=(
  23. 'Shows all migrations in the order they will be applied. '
  24. 'With a verbosity level of 2 or above all direct migration dependencies '
  25. 'and reverse dependencies (run_before) will be included.'
  26. )
  27. )
  28. parser.set_defaults(format='list')
  29. def handle(self, *args, **options):
  30. self.verbosity = options['verbosity']
  31. # Get the database we're operating from
  32. db = options['database']
  33. connection = connections[db]
  34. if options['format'] == "plan":
  35. return self.show_plan(connection, options['app_label'])
  36. else:
  37. return self.show_list(connection, options['app_label'])
  38. def _validate_app_names(self, loader, app_names):
  39. invalid_apps = []
  40. for app_name in app_names:
  41. if app_name not in loader.migrated_apps:
  42. invalid_apps.append(app_name)
  43. if invalid_apps:
  44. raise CommandError('No migrations present for: %s' % (', '.join(sorted(invalid_apps))))
  45. def show_list(self, connection, app_names=None):
  46. """
  47. Show a list of all migrations on the system, or only those of
  48. some named apps.
  49. """
  50. # Load migrations from disk/DB
  51. loader = MigrationLoader(connection, ignore_no_migrations=True)
  52. graph = loader.graph
  53. # If we were passed a list of apps, validate it
  54. if app_names:
  55. self._validate_app_names(loader, app_names)
  56. # Otherwise, show all apps in alphabetic order
  57. else:
  58. app_names = sorted(loader.migrated_apps)
  59. # For each app, print its migrations in order from oldest (roots) to
  60. # newest (leaves).
  61. for app_name in app_names:
  62. self.stdout.write(app_name, self.style.MIGRATE_LABEL)
  63. shown = set()
  64. for node in graph.leaf_nodes(app_name):
  65. for plan_node in graph.forwards_plan(node):
  66. if plan_node not in shown and plan_node[0] == app_name:
  67. # Give it a nice title if it's a squashed one
  68. title = plan_node[1]
  69. if graph.nodes[plan_node].replaces:
  70. title += " (%s squashed migrations)" % len(graph.nodes[plan_node].replaces)
  71. # Mark it as applied/unapplied
  72. if plan_node in loader.applied_migrations:
  73. self.stdout.write(" [X] %s" % title)
  74. else:
  75. self.stdout.write(" [ ] %s" % title)
  76. shown.add(plan_node)
  77. # If we didn't print anything, then a small message
  78. if not shown:
  79. self.stdout.write(" (no migrations)", self.style.ERROR)
  80. def show_plan(self, connection, app_names=None):
  81. """
  82. Show all known migrations (or only those of the specified app_names)
  83. in the order they will be applied.
  84. """
  85. # Load migrations from disk/DB
  86. loader = MigrationLoader(connection)
  87. graph = loader.graph
  88. if app_names:
  89. self._validate_app_names(loader, app_names)
  90. targets = [key for key in graph.leaf_nodes() if key[0] in app_names]
  91. else:
  92. targets = graph.leaf_nodes()
  93. plan = []
  94. seen = set()
  95. # Generate the plan
  96. for target in targets:
  97. for migration in graph.forwards_plan(target):
  98. if migration not in seen:
  99. node = graph.node_map[migration]
  100. plan.append(node)
  101. seen.add(migration)
  102. # Output
  103. def print_deps(node):
  104. out = []
  105. for parent in sorted(node.parents):
  106. out.append("%s.%s" % parent.key)
  107. if out:
  108. return " ... (%s)" % ", ".join(out)
  109. return ""
  110. for node in plan:
  111. deps = ""
  112. if self.verbosity >= 2:
  113. deps = print_deps(node)
  114. if node.key in loader.applied_migrations:
  115. self.stdout.write("[X] %s.%s%s" % (node.key[0], node.key[1], deps))
  116. else:
  117. self.stdout.write("[ ] %s.%s%s" % (node.key[0], node.key[1], deps))