south.migration.migrators: 231 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/south/migration/migrators.py

Stats: 0 executed, 216 missed, 15 excluded, 129 ignored

  1. from copy import copy, deepcopy
  2. from cStringIO import StringIO
  3. import datetime
  4. import inspect
  5. import sys
  6. import traceback
  7. from django.core.management import call_command
  8. from django.core.management.commands import loaddata
  9. from django.db import models
  10. import south.db
  11. from south import exceptions
  12. from south.db import DEFAULT_DB_ALIAS
  13. from south.models import MigrationHistory
  14. from south.signals import ran_migration
  15. class Migrator(object):
  16. def __init__(self, verbosity=0, interactive=False):
  17. self.verbosity = int(verbosity)
  18. self.interactive = bool(interactive)
  19. @staticmethod
  20. def title(target):
  21. raise NotImplementedError()
  22. def print_title(self, target):
  23. if self.verbosity:
  24. print self.title(target)
  25. @staticmethod
  26. def status(target):
  27. raise NotImplementedError()
  28. def print_status(self, migration):
  29. status = self.status(migration)
  30. if self.verbosity and status:
  31. print status
  32. @staticmethod
  33. def orm(migration):
  34. raise NotImplementedError()
  35. def backwards(self, migration):
  36. return self._wrap_direction(migration.backwards(), migration.prev_orm())
  37. def direction(self, migration):
  38. raise NotImplementedError()
  39. @staticmethod
  40. def _wrap_direction(direction, orm):
  41. args = inspect.getargspec(direction)
  42. if len(args[0]) == 1:
  43. # Old migration, no ORM should be passed in
  44. return direction
  45. return (lambda: direction(orm))
  46. @staticmethod
  47. def record(migration, database):
  48. raise NotImplementedError()
  49. def run_migration_error(self, migration, extra_info=''):
  50. return (
  51. ' ! Error found during real run of migration! Aborting.\n'
  52. '\n'
  53. ' ! Since you have a database that does not support running\n'
  54. ' ! schema-altering statements in transactions, we have had \n'
  55. ' ! to leave it in an interim state between migrations.\n'
  56. '%s\n'
  57. ' ! The South developers regret this has happened, and would\n'
  58. ' ! like to gently persuade you to consider a slightly\n'
  59. ' ! easier-to-deal-with DBMS (one that supports DDL transactions)\n'
  60. ' ! NOTE: The error which caused the migration to fail is further up.'
  61. ) % extra_info
  62. def run_migration(self, migration):
  63. migration_function = self.direction(migration)
  64. south.db.db.start_transaction()
  65. try:
  66. migration_function()
  67. south.db.db.execute_deferred_sql()
  68. except:
  69. south.db.db.rollback_transaction()
  70. if not south.db.db.has_ddl_transactions:
  71. print self.run_migration_error(migration)
  72. print "Error in migration: %s" % migration
  73. raise
  74. else:
  75. try:
  76. south.db.db.commit_transaction()
  77. except:
  78. print "Error during commit in migration: %s" % migration
  79. raise
  80. def run(self, migration):
  81. # Get the correct ORM.
  82. south.db.db.current_orm = self.orm(migration)
  83. # If we're not already in a dry run, and the database doesn't support
  84. # running DDL inside a transaction, *cough*MySQL*cough* then do a dry
  85. # run first.
  86. if not isinstance(getattr(self, '_wrapper', self), DryRunMigrator):
  87. if not south.db.db.has_ddl_transactions:
  88. dry_run = DryRunMigrator(migrator=self, ignore_fail=False)
  89. dry_run.run_migration(migration)
  90. return self.run_migration(migration)
  91. def done_migrate(self, migration, database):
  92. south.db.db.start_transaction()
  93. try:
  94. # Record us as having done this
  95. self.record(migration, database)
  96. except:
  97. south.db.db.rollback_transaction()
  98. raise
  99. else:
  100. south.db.db.commit_transaction()
  101. def send_ran_migration(self, migration):
  102. ran_migration.send(None,
  103. app=migration.app_label(),
  104. migration=migration,
  105. method=self.__class__.__name__.lower())
  106. def migrate(self, migration, database):
  107. """
  108. Runs the specified migration forwards/backwards, in order.
  109. """
  110. app = migration.migrations._migrations
  111. migration_name = migration.name()
  112. self.print_status(migration)
  113. result = self.run(migration)
  114. self.done_migrate(migration, database)
  115. self.send_ran_migration(migration)
  116. return result
  117. def migrate_many(self, target, migrations, database):
  118. raise NotImplementedError()
  119. class MigratorWrapper(object):
  120. def __init__(self, migrator, *args, **kwargs):
  121. self._migrator = copy(migrator)
  122. attributes = dict([(k, getattr(self, k))
  123. for k in self.__class__.__dict__.iterkeys()
  124. if not k.startswith('__')])
  125. self._migrator.__dict__.update(attributes)
  126. self._migrator.__dict__['_wrapper'] = self
  127. def __getattr__(self, name):
  128. return getattr(self._migrator, name)
  129. class DryRunMigrator(MigratorWrapper):
  130. def __init__(self, ignore_fail=True, *args, **kwargs):
  131. super(DryRunMigrator, self).__init__(*args, **kwargs)
  132. self._ignore_fail = ignore_fail
  133. def _run_migration(self, migration):
  134. if migration.no_dry_run():
  135. if self.verbosity:
  136. print " - Migration '%s' is marked for no-dry-run." % migration
  137. return
  138. south.db.db.dry_run = True
  139. # preserve the constraint cache as it can be mutated by the dry run
  140. constraint_cache = deepcopy(south.db.db._constraint_cache)
  141. if self._ignore_fail:
  142. south.db.db.debug, old_debug = False, south.db.db.debug
  143. pending_creates = south.db.db.get_pending_creates()
  144. south.db.db.start_transaction()
  145. migration_function = self.direction(migration)
  146. try:
  147. try:
  148. migration_function()
  149. south.db.db.execute_deferred_sql()
  150. except:
  151. raise exceptions.FailedDryRun(migration, sys.exc_info())
  152. finally:
  153. south.db.db.rollback_transactions_dry_run()
  154. if self._ignore_fail:
  155. south.db.db.debug = old_debug
  156. south.db.db.clear_run_data(pending_creates)
  157. south.db.db.dry_run = False
  158. # restore the preserved constraint cache from before dry run was
  159. # executed
  160. south.db.db._constraint_cache = constraint_cache
  161. def run_migration(self, migration):
  162. try:
  163. self._run_migration(migration)
  164. except exceptions.FailedDryRun:
  165. if self._ignore_fail:
  166. return False
  167. raise
  168. def done_migrate(self, *args, **kwargs):
  169. pass
  170. def send_ran_migration(self, *args, **kwargs):
  171. pass
  172. class FakeMigrator(MigratorWrapper):
  173. def run(self, migration):
  174. if self.verbosity:
  175. print ' (faked)'
  176. def send_ran_migration(self, *args, **kwargs):
  177. pass
  178. class LoadInitialDataMigrator(MigratorWrapper):
  179. def load_initial_data(self, target, db='default'):
  180. if target is None or target != target.migrations[-1]:
  181. return
  182. # Load initial data, if we ended up at target
  183. if self.verbosity:
  184. print " - Loading initial data for %s." % target.app_label()
  185. # Override Django's get_apps call temporarily to only load from the
  186. # current app
  187. old_get_apps = models.get_apps
  188. new_get_apps = lambda: [models.get_app(target.app_label())]
  189. models.get_apps = new_get_apps
  190. loaddata.get_apps = new_get_apps
  191. try:
  192. call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=db)
  193. finally:
  194. models.get_apps = old_get_apps
  195. loaddata.get_apps = old_get_apps
  196. def migrate_many(self, target, migrations, database):
  197. migrator = self._migrator
  198. result = migrator.__class__.migrate_many(migrator, target, migrations, database)
  199. if result:
  200. self.load_initial_data(target, db=database)
  201. return True
  202. class Forwards(Migrator):
  203. """
  204. Runs the specified migration forwards, in order.
  205. """
  206. torun = 'forwards'
  207. @staticmethod
  208. def title(target):
  209. if target is not None:
  210. return " - Migrating forwards to %s." % target.name()
  211. else:
  212. assert False, "You cannot migrate forwards to zero."
  213. @staticmethod
  214. def status(migration):
  215. return ' > %s' % migration
  216. @staticmethod
  217. def orm(migration):
  218. return migration.orm()
  219. def forwards(self, migration):
  220. return self._wrap_direction(migration.forwards(), migration.orm())
  221. direction = forwards
  222. @staticmethod
  223. def record(migration, database):
  224. # Record us as having done this
  225. record = MigrationHistory.for_migration(migration, database)
  226. try:
  227. from django.utils.timezone import now
  228. record.applied = now()
  229. except ImportError:
  230. record.applied = datetime.datetime.utcnow()
  231. if database != DEFAULT_DB_ALIAS:
  232. record.save(using=database)
  233. else:
  234. # Django 1.1 and below always go down this branch.
  235. record.save()
  236. def format_backwards(self, migration):
  237. if migration.no_dry_run():
  238. return " (migration cannot be dry-run; cannot discover commands)"
  239. old_debug, old_dry_run = south.db.db.debug, south.db.db.dry_run
  240. south.db.db.debug = south.db.db.dry_run = True
  241. stdout = sys.stdout
  242. sys.stdout = StringIO()
  243. try:
  244. try:
  245. self.backwards(migration)()
  246. return sys.stdout.getvalue()
  247. except:
  248. raise
  249. finally:
  250. south.db.db.debug, south.db.db.dry_run = old_debug, old_dry_run
  251. sys.stdout = stdout
  252. def run_migration_error(self, migration, extra_info=''):
  253. extra_info = ('\n'
  254. '! You *might* be able to recover with:'
  255. '%s'
  256. '%s' %
  257. (self.format_backwards(migration), extra_info))
  258. return super(Forwards, self).run_migration_error(migration, extra_info)
  259. def migrate_many(self, target, migrations, database):
  260. try:
  261. for migration in migrations:
  262. result = self.migrate(migration, database)
  263. if result is False: # The migrations errored, but nicely.
  264. return False
  265. finally:
  266. # Call any pending post_syncdb signals
  267. south.db.db.send_pending_create_signals(verbosity=self.verbosity,
  268. interactive=self.interactive)
  269. return True
  270. class Backwards(Migrator):
  271. """
  272. Runs the specified migration backwards, in order.
  273. """
  274. torun = 'backwards'
  275. @staticmethod
  276. def title(target):
  277. if target is None:
  278. return " - Migrating backwards to zero state."
  279. else:
  280. return " - Migrating backwards to just after %s." % target.name()
  281. @staticmethod
  282. def status(migration):
  283. return ' < %s' % migration
  284. @staticmethod
  285. def orm(migration):
  286. return migration.prev_orm()
  287. direction = Migrator.backwards
  288. @staticmethod
  289. def record(migration, database):
  290. # Record us as having not done this
  291. record = MigrationHistory.for_migration(migration, database)
  292. if record.id is not None:
  293. if database != DEFAULT_DB_ALIAS:
  294. record.delete(using=database)
  295. else:
  296. # Django 1.1 always goes down here
  297. record.delete()
  298. def migrate_many(self, target, migrations, database):
  299. for migration in migrations:
  300. self.migrate(migration, database)
  301. return True