Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from contextlib import contextmanager 

2import textwrap 

3 

4from . import batch 

5from . import schemaobj 

6from .. import util 

7from ..util import sqla_compat 

8from ..util.compat import exec_ 

9from ..util.compat import inspect_formatargspec 

10from ..util.compat import inspect_getargspec 

11 

12__all__ = ("Operations", "BatchOperations") 

13 

14try: 

15 from sqlalchemy.sql.naming import conv 

16except: 

17 conv = None 

18 

19 

20class Operations(util.ModuleClsProxy): 

21 

22 """Define high level migration operations. 

23 

24 Each operation corresponds to some schema migration operation, 

25 executed against a particular :class:`.MigrationContext` 

26 which in turn represents connectivity to a database, 

27 or a file output stream. 

28 

29 While :class:`.Operations` is normally configured as 

30 part of the :meth:`.EnvironmentContext.run_migrations` 

31 method called from an ``env.py`` script, a standalone 

32 :class:`.Operations` instance can be 

33 made for use cases external to regular Alembic 

34 migrations by passing in a :class:`.MigrationContext`:: 

35 

36 from alembic.migration import MigrationContext 

37 from alembic.operations import Operations 

38 

39 conn = myengine.connect() 

40 ctx = MigrationContext.configure(conn) 

41 op = Operations(ctx) 

42 

43 op.alter_column("t", "c", nullable=True) 

44 

45 Note that as of 0.8, most of the methods on this class are produced 

46 dynamically using the :meth:`.Operations.register_operation` 

47 method. 

48 

49 """ 

50 

51 _to_impl = util.Dispatcher() 

52 

53 def __init__(self, migration_context, impl=None): 

54 """Construct a new :class:`.Operations` 

55 

56 :param migration_context: a :class:`.MigrationContext` 

57 instance. 

58 

59 """ 

60 self.migration_context = migration_context 

61 if impl is None: 

62 self.impl = migration_context.impl 

63 else: 

64 self.impl = impl 

65 

66 self.schema_obj = schemaobj.SchemaObjects(migration_context) 

67 

68 @classmethod 

69 def register_operation(cls, name, sourcename=None): 

70 """Register a new operation for this class. 

71 

72 This method is normally used to add new operations 

73 to the :class:`.Operations` class, and possibly the 

74 :class:`.BatchOperations` class as well. All Alembic migration 

75 operations are implemented via this system, however the system 

76 is also available as a public API to facilitate adding custom 

77 operations. 

78 

79 .. versionadded:: 0.8.0 

80 

81 .. seealso:: 

82 

83 :ref:`operation_plugins` 

84 

85 

86 """ 

87 

88 def register(op_cls): 

89 if sourcename is None: 

90 fn = getattr(op_cls, name) 

91 source_name = fn.__name__ 

92 else: 

93 fn = getattr(op_cls, sourcename) 

94 source_name = fn.__name__ 

95 

96 spec = inspect_getargspec(fn) 

97 

98 name_args = spec[0] 

99 assert name_args[0:2] == ["cls", "operations"] 

100 

101 name_args[0:2] = ["self"] 

102 

103 args = inspect_formatargspec(*spec) 

104 num_defaults = len(spec[3]) if spec[3] else 0 

105 if num_defaults: 

106 defaulted_vals = name_args[0 - num_defaults :] 

107 else: 

108 defaulted_vals = () 

109 

110 apply_kw = inspect_formatargspec( 

111 name_args, 

112 spec[1], 

113 spec[2], 

114 defaulted_vals, 

115 formatvalue=lambda x: "=" + x, 

116 ) 

117 

118 func_text = textwrap.dedent( 

119 """\ 

120 def %(name)s%(args)s: 

121 %(doc)r 

122 return op_cls.%(source_name)s%(apply_kw)s 

123 """ 

124 % { 

125 "name": name, 

126 "source_name": source_name, 

127 "args": args, 

128 "apply_kw": apply_kw, 

129 "doc": fn.__doc__, 

130 "meth": fn.__name__, 

131 } 

132 ) 

133 globals_ = {"op_cls": op_cls} 

134 lcl = {} 

135 exec_(func_text, globals_, lcl) 

136 setattr(cls, name, lcl[name]) 

137 fn.__func__.__doc__ = ( 

138 "This method is proxied on " 

139 "the :class:`.%s` class, via the :meth:`.%s.%s` method." 

140 % (cls.__name__, cls.__name__, name) 

141 ) 

142 if hasattr(fn, "_legacy_translations"): 

143 lcl[name]._legacy_translations = fn._legacy_translations 

144 return op_cls 

145 

146 return register 

147 

148 @classmethod 

149 def implementation_for(cls, op_cls): 

150 """Register an implementation for a given :class:`.MigrateOperation`. 

151 

152 This is part of the operation extensibility API. 

153 

154 .. seealso:: 

155 

156 :ref:`operation_plugins` - example of use 

157 

158 """ 

159 

160 def decorate(fn): 

161 cls._to_impl.dispatch_for(op_cls)(fn) 

162 return fn 

163 

164 return decorate 

165 

166 @classmethod 

167 @contextmanager 

168 def context(cls, migration_context): 

169 op = Operations(migration_context) 

170 op._install_proxy() 

171 yield op 

172 op._remove_proxy() 

173 

174 @contextmanager 

175 def batch_alter_table( 

176 self, 

177 table_name, 

178 schema=None, 

179 recreate="auto", 

180 partial_reordering=None, 

181 copy_from=None, 

182 table_args=(), 

183 table_kwargs=util.immutabledict(), 

184 reflect_args=(), 

185 reflect_kwargs=util.immutabledict(), 

186 naming_convention=None, 

187 ): 

188 """Invoke a series of per-table migrations in batch. 

189 

190 Batch mode allows a series of operations specific to a table 

191 to be syntactically grouped together, and allows for alternate 

192 modes of table migration, in particular the "recreate" style of 

193 migration required by SQLite. 

194 

195 "recreate" style is as follows: 

196 

197 1. A new table is created with the new specification, based on the 

198 migration directives within the batch, using a temporary name. 

199 

200 2. the data copied from the existing table to the new table. 

201 

202 3. the existing table is dropped. 

203 

204 4. the new table is renamed to the existing table name. 

205 

206 The directive by default will only use "recreate" style on the 

207 SQLite backend, and only if directives are present which require 

208 this form, e.g. anything other than ``add_column()``. The batch 

209 operation on other backends will proceed using standard ALTER TABLE 

210 operations. 

211 

212 The method is used as a context manager, which returns an instance 

213 of :class:`.BatchOperations`; this object is the same as 

214 :class:`.Operations` except that table names and schema names 

215 are omitted. E.g.:: 

216 

217 with op.batch_alter_table("some_table") as batch_op: 

218 batch_op.add_column(Column('foo', Integer)) 

219 batch_op.drop_column('bar') 

220 

221 The operations within the context manager are invoked at once 

222 when the context is ended. When run against SQLite, if the 

223 migrations include operations not supported by SQLite's ALTER TABLE, 

224 the entire table will be copied to a new one with the new 

225 specification, moving all data across as well. 

226 

227 The copy operation by default uses reflection to retrieve the current 

228 structure of the table, and therefore :meth:`.batch_alter_table` 

229 in this mode requires that the migration is run in "online" mode. 

230 The ``copy_from`` parameter may be passed which refers to an existing 

231 :class:`.Table` object, which will bypass this reflection step. 

232 

233 .. note:: The table copy operation will currently not copy 

234 CHECK constraints, and may not copy UNIQUE constraints that are 

235 unnamed, as is possible on SQLite. See the section 

236 :ref:`sqlite_batch_constraints` for workarounds. 

237 

238 :param table_name: name of table 

239 :param schema: optional schema name. 

240 :param recreate: under what circumstances the table should be 

241 recreated. At its default of ``"auto"``, the SQLite dialect will 

242 recreate the table if any operations other than ``add_column()``, 

243 ``create_index()``, or ``drop_index()`` are 

244 present. Other options include ``"always"`` and ``"never"``. 

245 :param copy_from: optional :class:`~sqlalchemy.schema.Table` object 

246 that will act as the structure of the table being copied. If omitted, 

247 table reflection is used to retrieve the structure of the table. 

248 

249 .. versionadded:: 0.7.6 Fully implemented the 

250 :paramref:`~.Operations.batch_alter_table.copy_from` 

251 parameter. 

252 

253 .. seealso:: 

254 

255 :ref:`batch_offline_mode` 

256 

257 :paramref:`~.Operations.batch_alter_table.reflect_args` 

258 

259 :paramref:`~.Operations.batch_alter_table.reflect_kwargs` 

260 

261 :param reflect_args: a sequence of additional positional arguments that 

262 will be applied to the table structure being reflected / copied; 

263 this may be used to pass column and constraint overrides to the 

264 table that will be reflected, in lieu of passing the whole 

265 :class:`~sqlalchemy.schema.Table` using 

266 :paramref:`~.Operations.batch_alter_table.copy_from`. 

267 

268 .. versionadded:: 0.7.1 

269 

270 :param reflect_kwargs: a dictionary of additional keyword arguments 

271 that will be applied to the table structure being copied; this may be 

272 used to pass additional table and reflection options to the table that 

273 will be reflected, in lieu of passing the whole 

274 :class:`~sqlalchemy.schema.Table` using 

275 :paramref:`~.Operations.batch_alter_table.copy_from`. 

276 

277 .. versionadded:: 0.7.1 

278 

279 :param table_args: a sequence of additional positional arguments that 

280 will be applied to the new :class:`~sqlalchemy.schema.Table` when 

281 created, in addition to those copied from the source table. 

282 This may be used to provide additional constraints such as CHECK 

283 constraints that may not be reflected. 

284 :param table_kwargs: a dictionary of additional keyword arguments 

285 that will be applied to the new :class:`~sqlalchemy.schema.Table` 

286 when created, in addition to those copied from the source table. 

287 This may be used to provide for additional table options that may 

288 not be reflected. 

289 

290 .. versionadded:: 0.7.0 

291 

292 :param naming_convention: a naming convention dictionary of the form 

293 described at :ref:`autogen_naming_conventions` which will be applied 

294 to the :class:`~sqlalchemy.schema.MetaData` during the reflection 

295 process. This is typically required if one wants to drop SQLite 

296 constraints, as these constraints will not have names when 

297 reflected on this backend. Requires SQLAlchemy **0.9.4** or greater. 

298 

299 .. seealso:: 

300 

301 :ref:`dropping_sqlite_foreign_keys` 

302 

303 .. versionadded:: 0.7.1 

304 

305 :param partial_reordering: a list of tuples, each suggesting a desired 

306 ordering of two or more columns in the newly created table. Requires 

307 that :paramref:`.batch_alter_table.recreate` is set to ``"always"``. 

308 Examples, given a table with columns "a", "b", "c", and "d": 

309 

310 Specify the order of all columns:: 

311 

312 with op.batch_alter_table( 

313 "some_table", recreate="always", 

314 partial_reordering=[("c", "d", "a", "b")] 

315 ) as batch_op: 

316 pass 

317 

318 Ensure "d" appears before "c", and "b", appears before "a":: 

319 

320 with op.batch_alter_table( 

321 "some_table", recreate="always", 

322 partial_reordering=[("d", "c"), ("b", "a")] 

323 ) as batch_op: 

324 pass 

325 

326 The ordering of columns not included in the partial_reordering 

327 set is undefined. Therefore it is best to specify the complete 

328 ordering of all columns for best results. 

329 

330 .. versionadded:: 1.4.0 

331 

332 .. note:: batch mode requires SQLAlchemy 0.8 or above. 

333 

334 .. seealso:: 

335 

336 :ref:`batch_migrations` 

337 

338 """ 

339 impl = batch.BatchOperationsImpl( 

340 self, 

341 table_name, 

342 schema, 

343 recreate, 

344 copy_from, 

345 table_args, 

346 table_kwargs, 

347 reflect_args, 

348 reflect_kwargs, 

349 naming_convention, 

350 partial_reordering, 

351 ) 

352 batch_op = BatchOperations(self.migration_context, impl=impl) 

353 yield batch_op 

354 impl.flush() 

355 

356 def get_context(self): 

357 """Return the :class:`.MigrationContext` object that's 

358 currently in use. 

359 

360 """ 

361 

362 return self.migration_context 

363 

364 def invoke(self, operation): 

365 """Given a :class:`.MigrateOperation`, invoke it in terms of 

366 this :class:`.Operations` instance. 

367 

368 .. versionadded:: 0.8.0 

369 

370 """ 

371 fn = self._to_impl.dispatch( 

372 operation, self.migration_context.impl.__dialect__ 

373 ) 

374 return fn(self, operation) 

375 

376 def f(self, name): 

377 """Indicate a string name that has already had a naming convention 

378 applied to it. 

379 

380 This feature combines with the SQLAlchemy ``naming_convention`` feature 

381 to disambiguate constraint names that have already had naming 

382 conventions applied to them, versus those that have not. This is 

383 necessary in the case that the ``"%(constraint_name)s"`` token 

384 is used within a naming convention, so that it can be identified 

385 that this particular name should remain fixed. 

386 

387 If the :meth:`.Operations.f` is used on a constraint, the naming 

388 convention will not take effect:: 

389 

390 op.add_column('t', 'x', Boolean(name=op.f('ck_bool_t_x'))) 

391 

392 Above, the CHECK constraint generated will have the name 

393 ``ck_bool_t_x`` regardless of whether or not a naming convention is 

394 in use. 

395 

396 Alternatively, if a naming convention is in use, and 'f' is not used, 

397 names will be converted along conventions. If the ``target_metadata`` 

398 contains the naming convention 

399 ``{"ck": "ck_bool_%(table_name)s_%(constraint_name)s"}``, then the 

400 output of the following: 

401 

402 op.add_column('t', 'x', Boolean(name='x')) 

403 

404 will be:: 

405 

406 CONSTRAINT ck_bool_t_x CHECK (x in (1, 0))) 

407 

408 The function is rendered in the output of autogenerate when 

409 a particular constraint name is already converted, for SQLAlchemy 

410 version **0.9.4 and greater only**. Even though ``naming_convention`` 

411 was introduced in 0.9.2, the string disambiguation service is new 

412 as of 0.9.4. 

413 

414 .. versionadded:: 0.6.4 

415 

416 """ 

417 if conv: 

418 return conv(name) 

419 else: 

420 raise NotImplementedError( 

421 "op.f() feature requires SQLAlchemy 0.9.4 or greater." 

422 ) 

423 

424 def inline_literal(self, value, type_=None): 

425 r"""Produce an 'inline literal' expression, suitable for 

426 using in an INSERT, UPDATE, or DELETE statement. 

427 

428 When using Alembic in "offline" mode, CRUD operations 

429 aren't compatible with SQLAlchemy's default behavior surrounding 

430 literal values, 

431 which is that they are converted into bound values and passed 

432 separately into the ``execute()`` method of the DBAPI cursor. 

433 An offline SQL 

434 script needs to have these rendered inline. While it should 

435 always be noted that inline literal values are an **enormous** 

436 security hole in an application that handles untrusted input, 

437 a schema migration is not run in this context, so 

438 literals are safe to render inline, with the caveat that 

439 advanced types like dates may not be supported directly 

440 by SQLAlchemy. 

441 

442 See :meth:`.execute` for an example usage of 

443 :meth:`.inline_literal`. 

444 

445 The environment can also be configured to attempt to render 

446 "literal" values inline automatically, for those simple types 

447 that are supported by the dialect; see 

448 :paramref:`.EnvironmentContext.configure.literal_binds` for this 

449 more recently added feature. 

450 

451 :param value: The value to render. Strings, integers, and simple 

452 numerics should be supported. Other types like boolean, 

453 dates, etc. may or may not be supported yet by various 

454 backends. 

455 :param type\_: optional - a :class:`sqlalchemy.types.TypeEngine` 

456 subclass stating the type of this value. In SQLAlchemy 

457 expressions, this is usually derived automatically 

458 from the Python type of the value itself, as well as 

459 based on the context in which the value is used. 

460 

461 .. seealso:: 

462 

463 :paramref:`.EnvironmentContext.configure.literal_binds` 

464 

465 """ 

466 return sqla_compat._literal_bindparam(None, value, type_=type_) 

467 

468 def get_bind(self): 

469 """Return the current 'bind'. 

470 

471 Under normal circumstances, this is the 

472 :class:`~sqlalchemy.engine.Connection` currently being used 

473 to emit SQL to the database. 

474 

475 In a SQL script context, this value is ``None``. [TODO: verify this] 

476 

477 """ 

478 return self.migration_context.impl.bind 

479 

480 

481class BatchOperations(Operations): 

482 """Modifies the interface :class:`.Operations` for batch mode. 

483 

484 This basically omits the ``table_name`` and ``schema`` parameters 

485 from associated methods, as these are a given when running under batch 

486 mode. 

487 

488 .. seealso:: 

489 

490 :meth:`.Operations.batch_alter_table` 

491 

492 Note that as of 0.8, most of the methods on this class are produced 

493 dynamically using the :meth:`.Operations.register_operation` 

494 method. 

495 

496 """ 

497 

498 def _noop(self, operation): 

499 raise NotImplementedError( 

500 "The %s method does not apply to a batch table alter operation." 

501 % operation 

502 )