@@ -912,12 +912,14 @@ user input. HIST is a variable to store history of choices."
912912(defun djangonaut-find-migration ()
913913 " Open definition of the Django migration."
914914 (interactive )
915- (djangonaut-find-file-and-line #'find-file " Migration: " (djangonaut-get-migrations) 'djangonaut-migrations-history ))
915+ (djangonaut-find-file-and-line #'find-file " Migration: " (djangonaut-get-migrations) 'djangonaut-migrations-history )
916+ (djangonaut-migration-mode +1 ))
916917
917918(defun djangonaut-find-migration-other-window ()
918919 " Open definition of the Django migration in the other window."
919920 (interactive )
920- (djangonaut-find-file-and-line #'find-file-other-window " Migration: " (djangonaut-get-migrations) 'djangonaut-migrations-history ))
921+ (djangonaut-find-file-and-line #'find-file-other-window " Migration: " (djangonaut-get-migrations) 'djangonaut-migrations-history )
922+ (djangonaut-migration-mode +1 ))
921923
922924(defun djangonaut-find-sql-function ()
923925 " Open definition of the Django sql function."
@@ -1181,6 +1183,156 @@ user input. HIST is a variable to store history of choices."
11811183 (djangonaut-mode 1 )))))))
11821184 :require 'djangonaut )
11831185
1186+ (defvar djangonaut-get-previous-migration-code "
1187+ from inspect import getsourcefile, findsource
1188+
1189+ from django.db.migrations.loader import MigrationLoader
1190+
1191+ loader = MigrationLoader(connection=None, load=False)
1192+ loader.load_disk()
1193+
1194+ for (label, module_name), migration in sorted(loader.disk_migrations.items()):
1195+ Migration = migration.__class__
1196+ migration_file = getsourcefile(Migration)
1197+ if migration_file == sys.argv[-1]:
1198+ dependencies = [loader.disk_migrations[i] for i in Migration.dependencies]
1199+ files = {getsourcefile(dep.__class__): dep.__class__ for dep in dependencies}
1200+ longest = max(files, key=lambda x: len(os.path.commonprefix([migration_file, x])))
1201+ dependency = files[longest]
1202+ result[getsourcefile(dependency)] = findsource(dependency)[1]
1203+ break
1204+
1205+ " " Python source code to get previous migration file." )
1206+
1207+ (defvar djangonaut-get-next-migration-code "
1208+ from inspect import getsourcefile, findsource
1209+
1210+ from django.db.migrations.loader import MigrationLoader
1211+
1212+ loader = MigrationLoader(connection=None, load=False)
1213+ loader.load_disk()
1214+
1215+ for (label, module_name), migration in sorted(loader.disk_migrations.items()):
1216+ Migration = migration.__class__
1217+ migration_file = getsourcefile(Migration)
1218+ if migration_file == sys.argv[-1]:
1219+ have_dependency = [other for other in loader.disk_migrations.values() if (label, module_name) in other.dependencies]
1220+ files = {getsourcefile(other.__class__): other.__class__ for other in have_dependency}
1221+ longest = max(files, key=lambda x: len(os.path.commonprefix([migration_file, x])))
1222+ parent = files[longest]
1223+ result[getsourcefile(parent)] = findsource(parent)[1]
1224+ break
1225+
1226+ " " Python source code to get next migration file." )
1227+
1228+ (defvar djangonaut-rerun-migration-code "
1229+ from __future__ import print_function
1230+
1231+ from django.apps import apps
1232+ from django.conf import settings
1233+ apps.populate(settings.INSTALLED_APPS)
1234+
1235+ import sys
1236+ from inspect import getsourcefile
1237+
1238+ from django.core.management import call_command
1239+ from django.db.migrations.loader import MigrationLoader
1240+
1241+ loader = MigrationLoader(connection=None, load=False)
1242+ loader.load_disk()
1243+
1244+ for (label, module_name), migration in sorted(loader.disk_migrations.items()):
1245+ Migration = migration.__class__
1246+ if getsourcefile(Migration) == sys.argv[-1]:
1247+ for app_name, migration_name in Migration.dependencies:
1248+ call_command('migrate', app_name, migration_name)
1249+ break
1250+
1251+ call_command('migrate')
1252+
1253+ " " Python source code to rerun migration file." )
1254+
1255+ (defun djangonaut-get-previous-migration (filename )
1256+ " Execute and parse python code to get previous migration FILENAME."
1257+ (djangonaut-read (djangonaut-call djangonaut-get-previous-migration-code filename)))
1258+
1259+ (defun djangonaut-get-next-migration (filename )
1260+ " Execute and parse python code to get next migration FILENAME."
1261+ (djangonaut-read (djangonaut-call djangonaut-get-next-migration-code filename)))
1262+
1263+ (defun djangonaut-find-previous-migration (filename )
1264+ " "
1265+ (interactive (list (buffer-file-name )))
1266+ (let ((migration (djangonaut-get-previous-migration
1267+ (pythonic-python-readable-file-name filename))))
1268+ (when migration
1269+ ; ; FIXME: Copy-pasted from `djangonaut-find-file-and-line' .
1270+ (let* ((value (caar migration))
1271+ (lineno (cadr migration)))
1272+ (apply #'find-file (pythonic-emacs-readable-file-name value) nil )
1273+ (goto-char (point-min ))
1274+ (forward-line lineno)
1275+ (run-hooks 'djangonaut-navigate-line-hook )
1276+ (djangonaut-migration-mode +1 )))))
1277+
1278+ (defun djangonaut-find-next-migration (filename )
1279+ " "
1280+ (interactive (list (buffer-file-name )))
1281+ (let ((migration (djangonaut-get-next-migration
1282+ (pythonic-python-readable-file-name filename))))
1283+ (when migration
1284+ ; ; FIXME: Copy-pasted from `djangonaut-find-file-and-line' .
1285+ (let* ((value (caar migration))
1286+ (lineno (cadr migration)))
1287+ (apply #'find-file (pythonic-emacs-readable-file-name value) nil )
1288+ (goto-char (point-min ))
1289+ (forward-line lineno)
1290+ (run-hooks 'djangonaut-navigate-line-hook )
1291+ (djangonaut-migration-mode +1 )))))
1292+
1293+ (defun djangonaut-rerun-current-migration (filename )
1294+ " Reapply migration from the FILENAME."
1295+ (interactive (list (buffer-file-name )))
1296+ ; ; FIXME: Copy-pasted from run command.
1297+ (let* ((buffer (get-buffer-create " *Django*" ))
1298+ (process (get-buffer-process buffer)))
1299+ (when (and process (process-live-p process))
1300+ (setq buffer (generate-new-buffer " *Django*" )))
1301+ (with-current-buffer buffer
1302+ (hack-dir-local-variables-non-file-buffer )
1303+ (pythonic-start-process :process " djangonaut"
1304+ :buffer buffer
1305+ :args (list " -c" djangonaut-rerun-migration-code (pythonic-python-readable-file-name filename))
1306+ :cwd (pythonic-emacs-readable-file-name (djangonaut-get-project-root))
1307+ :filter (lambda (process string )
1308+ (comint-output-filter process (ansi-color-apply string))))
1309+ (let ((inhibit-read-only t ))
1310+ (erase-buffer ))
1311+ (comint-mode )
1312+ (setq-local comint-prompt-read-only t )
1313+ (pop-to-buffer buffer))))
1314+
1315+ (defvar djangonaut-migration-command-map
1316+ (let ((map (make-sparse-keymap )))
1317+ (define-key map (kbd " <" ) 'djangonaut-find-previous-migration )
1318+ (define-key map (kbd " >" ) 'djangonaut-find-next-migration )
1319+ (define-key map (kbd " @" ) 'djangonaut-rerun-current-migration )
1320+ map))
1321+
1322+ (defvar djangonaut-migration-mode-map
1323+ (let ((map (make-sparse-keymap )))
1324+ (define-key map djangonaut-keymap-prefix djangonaut-migration-command-map)
1325+ map))
1326+
1327+ (defvar djangonaut-migration-mode-lighter " " )
1328+
1329+ (define-minor-mode djangonaut-migration-mode
1330+ " Minor mode to interact with Django migration modules.
1331+
1332+ \\ {djangonaut-migration-mode-map}"
1333+ :lighter djangonaut-migration-mode-lighter
1334+ :keymap djangonaut-migration-mode-map)
1335+
11841336(provide 'djangonaut )
11851337
11861338; ;; djangonaut.el ends here
0 commit comments