ops.py 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048
  1. from .. import util
  2. from ..util import sqla_compat
  3. from . import schemaobj
  4. from sqlalchemy.types import NULLTYPE
  5. from .base import Operations, BatchOperations
  6. import re
  7. class MigrateOperation(object):
  8. """base class for migration command and organization objects.
  9. This system is part of the operation extensibility API.
  10. .. versionadded:: 0.8.0
  11. .. seealso::
  12. :ref:`operation_objects`
  13. :ref:`operation_plugins`
  14. :ref:`customizing_revision`
  15. """
  16. @util.memoized_property
  17. def info(self):
  18. """A dictionary that may be used to store arbitrary information
  19. along with this :class:`.MigrateOperation` object.
  20. """
  21. return {}
  22. class AddConstraintOp(MigrateOperation):
  23. """Represent an add constraint operation."""
  24. add_constraint_ops = util.Dispatcher()
  25. @property
  26. def constraint_type(self):
  27. raise NotImplementedError()
  28. @classmethod
  29. def register_add_constraint(cls, type_):
  30. def go(klass):
  31. cls.add_constraint_ops.dispatch_for(type_)(klass.from_constraint)
  32. return klass
  33. return go
  34. @classmethod
  35. def from_constraint(cls, constraint):
  36. return cls.add_constraint_ops.dispatch(
  37. constraint.__visit_name__)(constraint)
  38. def reverse(self):
  39. return DropConstraintOp.from_constraint(self.to_constraint())
  40. def to_diff_tuple(self):
  41. return ("add_constraint", self.to_constraint())
  42. @Operations.register_operation("drop_constraint")
  43. @BatchOperations.register_operation("drop_constraint", "batch_drop_constraint")
  44. class DropConstraintOp(MigrateOperation):
  45. """Represent a drop constraint operation."""
  46. def __init__(
  47. self,
  48. constraint_name, table_name, type_=None, schema=None,
  49. _orig_constraint=None):
  50. self.constraint_name = constraint_name
  51. self.table_name = table_name
  52. self.constraint_type = type_
  53. self.schema = schema
  54. self._orig_constraint = _orig_constraint
  55. def reverse(self):
  56. if self._orig_constraint is None:
  57. raise ValueError(
  58. "operation is not reversible; "
  59. "original constraint is not present")
  60. return AddConstraintOp.from_constraint(self._orig_constraint)
  61. def to_diff_tuple(self):
  62. if self.constraint_type == "foreignkey":
  63. return ("remove_fk", self.to_constraint())
  64. else:
  65. return ("remove_constraint", self.to_constraint())
  66. @classmethod
  67. def from_constraint(cls, constraint):
  68. types = {
  69. "unique_constraint": "unique",
  70. "foreign_key_constraint": "foreignkey",
  71. "primary_key_constraint": "primary",
  72. "check_constraint": "check",
  73. "column_check_constraint": "check",
  74. }
  75. constraint_table = sqla_compat._table_for_constraint(constraint)
  76. return cls(
  77. constraint.name,
  78. constraint_table.name,
  79. schema=constraint_table.schema,
  80. type_=types[constraint.__visit_name__],
  81. _orig_constraint=constraint
  82. )
  83. def to_constraint(self):
  84. if self._orig_constraint is not None:
  85. return self._orig_constraint
  86. else:
  87. raise ValueError(
  88. "constraint cannot be produced; "
  89. "original constraint is not present")
  90. @classmethod
  91. @util._with_legacy_names([
  92. ("type", "type_"),
  93. ("name", "constraint_name"),
  94. ])
  95. def drop_constraint(
  96. cls, operations, constraint_name, table_name,
  97. type_=None, schema=None):
  98. """Drop a constraint of the given name, typically via DROP CONSTRAINT.
  99. :param constraint_name: name of the constraint.
  100. :param table_name: table name.
  101. :param type_: optional, required on MySQL. can be
  102. 'foreignkey', 'primary', 'unique', or 'check'.
  103. :param schema: Optional schema name to operate within. To control
  104. quoting of the schema outside of the default behavior, use
  105. the SQLAlchemy construct
  106. :class:`~sqlalchemy.sql.elements.quoted_name`.
  107. .. versionadded:: 0.7.0 'schema' can now accept a
  108. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  109. .. versionchanged:: 0.8.0 The following positional argument names
  110. have been changed:
  111. * name -> constraint_name
  112. """
  113. op = cls(constraint_name, table_name, type_=type_, schema=schema)
  114. return operations.invoke(op)
  115. @classmethod
  116. def batch_drop_constraint(cls, operations, constraint_name, type_=None):
  117. """Issue a "drop constraint" instruction using the
  118. current batch migration context.
  119. The batch form of this call omits the ``table_name`` and ``schema``
  120. arguments from the call.
  121. .. seealso::
  122. :meth:`.Operations.drop_constraint`
  123. .. versionchanged:: 0.8.0 The following positional argument names
  124. have been changed:
  125. * name -> constraint_name
  126. """
  127. op = cls(
  128. constraint_name, operations.impl.table_name,
  129. type_=type_, schema=operations.impl.schema
  130. )
  131. return operations.invoke(op)
  132. @Operations.register_operation("create_primary_key")
  133. @BatchOperations.register_operation(
  134. "create_primary_key", "batch_create_primary_key")
  135. @AddConstraintOp.register_add_constraint("primary_key_constraint")
  136. class CreatePrimaryKeyOp(AddConstraintOp):
  137. """Represent a create primary key operation."""
  138. constraint_type = "primarykey"
  139. def __init__(
  140. self, constraint_name, table_name, columns,
  141. schema=None, _orig_constraint=None, **kw):
  142. self.constraint_name = constraint_name
  143. self.table_name = table_name
  144. self.columns = columns
  145. self.schema = schema
  146. self._orig_constraint = _orig_constraint
  147. self.kw = kw
  148. @classmethod
  149. def from_constraint(cls, constraint):
  150. constraint_table = sqla_compat._table_for_constraint(constraint)
  151. return cls(
  152. constraint.name,
  153. constraint_table.name,
  154. constraint.columns,
  155. schema=constraint_table.schema,
  156. _orig_constraint=constraint
  157. )
  158. def to_constraint(self, migration_context=None):
  159. if self._orig_constraint is not None:
  160. return self._orig_constraint
  161. schema_obj = schemaobj.SchemaObjects(migration_context)
  162. return schema_obj.primary_key_constraint(
  163. self.constraint_name, self.table_name,
  164. self.columns, schema=self.schema)
  165. @classmethod
  166. @util._with_legacy_names([
  167. ('name', 'constraint_name'),
  168. ('cols', 'columns')
  169. ])
  170. def create_primary_key(
  171. cls, operations,
  172. constraint_name, table_name, columns, schema=None):
  173. """Issue a "create primary key" instruction using the current
  174. migration context.
  175. e.g.::
  176. from alembic import op
  177. op.create_primary_key(
  178. "pk_my_table", "my_table",
  179. ["id", "version"]
  180. )
  181. This internally generates a :class:`~sqlalchemy.schema.Table` object
  182. containing the necessary columns, then generates a new
  183. :class:`~sqlalchemy.schema.PrimaryKeyConstraint`
  184. object which it then associates with the
  185. :class:`~sqlalchemy.schema.Table`.
  186. Any event listeners associated with this action will be fired
  187. off normally. The :class:`~sqlalchemy.schema.AddConstraint`
  188. construct is ultimately used to generate the ALTER statement.
  189. :param name: Name of the primary key constraint. The name is necessary
  190. so that an ALTER statement can be emitted. For setups that
  191. use an automated naming scheme such as that described at
  192. :ref:`sqla:constraint_naming_conventions`
  193. ``name`` here can be ``None``, as the event listener will
  194. apply the name to the constraint object when it is associated
  195. with the table.
  196. :param table_name: String name of the target table.
  197. :param columns: a list of string column names to be applied to the
  198. primary key constraint.
  199. :param schema: Optional schema name to operate within. To control
  200. quoting of the schema outside of the default behavior, use
  201. the SQLAlchemy construct
  202. :class:`~sqlalchemy.sql.elements.quoted_name`.
  203. .. versionadded:: 0.7.0 'schema' can now accept a
  204. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  205. .. versionchanged:: 0.8.0 The following positional argument names
  206. have been changed:
  207. * name -> constraint_name
  208. * cols -> columns
  209. """
  210. op = cls(constraint_name, table_name, columns, schema)
  211. return operations.invoke(op)
  212. @classmethod
  213. def batch_create_primary_key(cls, operations, constraint_name, columns):
  214. """Issue a "create primary key" instruction using the
  215. current batch migration context.
  216. The batch form of this call omits the ``table_name`` and ``schema``
  217. arguments from the call.
  218. .. seealso::
  219. :meth:`.Operations.create_primary_key`
  220. """
  221. op = cls(
  222. constraint_name, operations.impl.table_name, columns,
  223. schema=operations.impl.schema
  224. )
  225. return operations.invoke(op)
  226. @Operations.register_operation("create_unique_constraint")
  227. @BatchOperations.register_operation(
  228. "create_unique_constraint", "batch_create_unique_constraint")
  229. @AddConstraintOp.register_add_constraint("unique_constraint")
  230. class CreateUniqueConstraintOp(AddConstraintOp):
  231. """Represent a create unique constraint operation."""
  232. constraint_type = "unique"
  233. def __init__(
  234. self, constraint_name, table_name,
  235. columns, schema=None, _orig_constraint=None, **kw):
  236. self.constraint_name = constraint_name
  237. self.table_name = table_name
  238. self.columns = columns
  239. self.schema = schema
  240. self._orig_constraint = _orig_constraint
  241. self.kw = kw
  242. @classmethod
  243. def from_constraint(cls, constraint):
  244. constraint_table = sqla_compat._table_for_constraint(constraint)
  245. kw = {}
  246. if constraint.deferrable:
  247. kw['deferrable'] = constraint.deferrable
  248. if constraint.initially:
  249. kw['initially'] = constraint.initially
  250. return cls(
  251. constraint.name,
  252. constraint_table.name,
  253. [c.name for c in constraint.columns],
  254. schema=constraint_table.schema,
  255. _orig_constraint=constraint,
  256. **kw
  257. )
  258. def to_constraint(self, migration_context=None):
  259. if self._orig_constraint is not None:
  260. return self._orig_constraint
  261. schema_obj = schemaobj.SchemaObjects(migration_context)
  262. return schema_obj.unique_constraint(
  263. self.constraint_name, self.table_name, self.columns,
  264. schema=self.schema, **self.kw)
  265. @classmethod
  266. @util._with_legacy_names([
  267. ('name', 'constraint_name'),
  268. ('source', 'table_name'),
  269. ('local_cols', 'columns'),
  270. ])
  271. def create_unique_constraint(
  272. cls, operations, constraint_name, table_name, columns,
  273. schema=None, **kw):
  274. """Issue a "create unique constraint" instruction using the
  275. current migration context.
  276. e.g.::
  277. from alembic import op
  278. op.create_unique_constraint("uq_user_name", "user", ["name"])
  279. This internally generates a :class:`~sqlalchemy.schema.Table` object
  280. containing the necessary columns, then generates a new
  281. :class:`~sqlalchemy.schema.UniqueConstraint`
  282. object which it then associates with the
  283. :class:`~sqlalchemy.schema.Table`.
  284. Any event listeners associated with this action will be fired
  285. off normally. The :class:`~sqlalchemy.schema.AddConstraint`
  286. construct is ultimately used to generate the ALTER statement.
  287. :param name: Name of the unique constraint. The name is necessary
  288. so that an ALTER statement can be emitted. For setups that
  289. use an automated naming scheme such as that described at
  290. :ref:`sqla:constraint_naming_conventions`,
  291. ``name`` here can be ``None``, as the event listener will
  292. apply the name to the constraint object when it is associated
  293. with the table.
  294. :param table_name: String name of the source table.
  295. :param columns: a list of string column names in the
  296. source table.
  297. :param deferrable: optional bool. If set, emit DEFERRABLE or
  298. NOT DEFERRABLE when issuing DDL for this constraint.
  299. :param initially: optional string. If set, emit INITIALLY <value>
  300. when issuing DDL for this constraint.
  301. :param schema: Optional schema name to operate within. To control
  302. quoting of the schema outside of the default behavior, use
  303. the SQLAlchemy construct
  304. :class:`~sqlalchemy.sql.elements.quoted_name`.
  305. .. versionadded:: 0.7.0 'schema' can now accept a
  306. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  307. .. versionchanged:: 0.8.0 The following positional argument names
  308. have been changed:
  309. * name -> constraint_name
  310. * source -> table_name
  311. * local_cols -> columns
  312. """
  313. op = cls(
  314. constraint_name, table_name, columns,
  315. schema=schema, **kw
  316. )
  317. return operations.invoke(op)
  318. @classmethod
  319. @util._with_legacy_names([('name', 'constraint_name')])
  320. def batch_create_unique_constraint(
  321. cls, operations, constraint_name, columns, **kw):
  322. """Issue a "create unique constraint" instruction using the
  323. current batch migration context.
  324. The batch form of this call omits the ``source`` and ``schema``
  325. arguments from the call.
  326. .. seealso::
  327. :meth:`.Operations.create_unique_constraint`
  328. .. versionchanged:: 0.8.0 The following positional argument names
  329. have been changed:
  330. * name -> constraint_name
  331. """
  332. kw['schema'] = operations.impl.schema
  333. op = cls(
  334. constraint_name, operations.impl.table_name, columns,
  335. **kw
  336. )
  337. return operations.invoke(op)
  338. @Operations.register_operation("create_foreign_key")
  339. @BatchOperations.register_operation(
  340. "create_foreign_key", "batch_create_foreign_key")
  341. @AddConstraintOp.register_add_constraint("foreign_key_constraint")
  342. class CreateForeignKeyOp(AddConstraintOp):
  343. """Represent a create foreign key constraint operation."""
  344. constraint_type = "foreignkey"
  345. def __init__(
  346. self, constraint_name, source_table, referent_table, local_cols,
  347. remote_cols, _orig_constraint=None, **kw):
  348. self.constraint_name = constraint_name
  349. self.source_table = source_table
  350. self.referent_table = referent_table
  351. self.local_cols = local_cols
  352. self.remote_cols = remote_cols
  353. self._orig_constraint = _orig_constraint
  354. self.kw = kw
  355. def to_diff_tuple(self):
  356. return ("add_fk", self.to_constraint())
  357. @classmethod
  358. def from_constraint(cls, constraint):
  359. kw = {}
  360. if constraint.onupdate:
  361. kw['onupdate'] = constraint.onupdate
  362. if constraint.ondelete:
  363. kw['ondelete'] = constraint.ondelete
  364. if constraint.initially:
  365. kw['initially'] = constraint.initially
  366. if constraint.deferrable:
  367. kw['deferrable'] = constraint.deferrable
  368. if constraint.use_alter:
  369. kw['use_alter'] = constraint.use_alter
  370. source_schema, source_table, \
  371. source_columns, target_schema, \
  372. target_table, target_columns,\
  373. onupdate, ondelete, deferrable, initially \
  374. = sqla_compat._fk_spec(constraint)
  375. kw['source_schema'] = source_schema
  376. kw['referent_schema'] = target_schema
  377. return cls(
  378. constraint.name,
  379. source_table,
  380. target_table,
  381. source_columns,
  382. target_columns,
  383. _orig_constraint=constraint,
  384. **kw
  385. )
  386. def to_constraint(self, migration_context=None):
  387. if self._orig_constraint is not None:
  388. return self._orig_constraint
  389. schema_obj = schemaobj.SchemaObjects(migration_context)
  390. return schema_obj.foreign_key_constraint(
  391. self.constraint_name,
  392. self.source_table, self.referent_table,
  393. self.local_cols, self.remote_cols,
  394. **self.kw)
  395. @classmethod
  396. @util._with_legacy_names([
  397. ('name', 'constraint_name'),
  398. ('source', 'source_table'),
  399. ('referent', 'referent_table'),
  400. ])
  401. def create_foreign_key(cls, operations, constraint_name,
  402. source_table, referent_table, local_cols,
  403. remote_cols, onupdate=None, ondelete=None,
  404. deferrable=None, initially=None, match=None,
  405. source_schema=None, referent_schema=None,
  406. **dialect_kw):
  407. """Issue a "create foreign key" instruction using the
  408. current migration context.
  409. e.g.::
  410. from alembic import op
  411. op.create_foreign_key(
  412. "fk_user_address", "address",
  413. "user", ["user_id"], ["id"])
  414. This internally generates a :class:`~sqlalchemy.schema.Table` object
  415. containing the necessary columns, then generates a new
  416. :class:`~sqlalchemy.schema.ForeignKeyConstraint`
  417. object which it then associates with the
  418. :class:`~sqlalchemy.schema.Table`.
  419. Any event listeners associated with this action will be fired
  420. off normally. The :class:`~sqlalchemy.schema.AddConstraint`
  421. construct is ultimately used to generate the ALTER statement.
  422. :param name: Name of the foreign key constraint. The name is necessary
  423. so that an ALTER statement can be emitted. For setups that
  424. use an automated naming scheme such as that described at
  425. :ref:`sqla:constraint_naming_conventions`,
  426. ``name`` here can be ``None``, as the event listener will
  427. apply the name to the constraint object when it is associated
  428. with the table.
  429. :param source_table: String name of the source table.
  430. :param referent_table: String name of the destination table.
  431. :param local_cols: a list of string column names in the
  432. source table.
  433. :param remote_cols: a list of string column names in the
  434. remote table.
  435. :param onupdate: Optional string. If set, emit ON UPDATE <value> when
  436. issuing DDL for this constraint. Typical values include CASCADE,
  437. DELETE and RESTRICT.
  438. :param ondelete: Optional string. If set, emit ON DELETE <value> when
  439. issuing DDL for this constraint. Typical values include CASCADE,
  440. DELETE and RESTRICT.
  441. :param deferrable: optional bool. If set, emit DEFERRABLE or NOT
  442. DEFERRABLE when issuing DDL for this constraint.
  443. :param source_schema: Optional schema name of the source table.
  444. :param referent_schema: Optional schema name of the destination table.
  445. .. versionchanged:: 0.8.0 The following positional argument names
  446. have been changed:
  447. * name -> constraint_name
  448. * source -> source_table
  449. * referent -> referent_table
  450. """
  451. op = cls(
  452. constraint_name,
  453. source_table, referent_table,
  454. local_cols, remote_cols,
  455. onupdate=onupdate, ondelete=ondelete,
  456. deferrable=deferrable,
  457. source_schema=source_schema,
  458. referent_schema=referent_schema,
  459. initially=initially, match=match,
  460. **dialect_kw
  461. )
  462. return operations.invoke(op)
  463. @classmethod
  464. @util._with_legacy_names([
  465. ('name', 'constraint_name'),
  466. ('referent', 'referent_table')
  467. ])
  468. def batch_create_foreign_key(
  469. cls, operations, constraint_name, referent_table,
  470. local_cols, remote_cols,
  471. referent_schema=None,
  472. onupdate=None, ondelete=None,
  473. deferrable=None, initially=None, match=None,
  474. **dialect_kw):
  475. """Issue a "create foreign key" instruction using the
  476. current batch migration context.
  477. The batch form of this call omits the ``source`` and ``source_schema``
  478. arguments from the call.
  479. e.g.::
  480. with batch_alter_table("address") as batch_op:
  481. batch_op.create_foreign_key(
  482. "fk_user_address",
  483. "user", ["user_id"], ["id"])
  484. .. seealso::
  485. :meth:`.Operations.create_foreign_key`
  486. .. versionchanged:: 0.8.0 The following positional argument names
  487. have been changed:
  488. * name -> constraint_name
  489. * referent -> referent_table
  490. """
  491. op = cls(
  492. constraint_name,
  493. operations.impl.table_name, referent_table,
  494. local_cols, remote_cols,
  495. onupdate=onupdate, ondelete=ondelete,
  496. deferrable=deferrable,
  497. source_schema=operations.impl.schema,
  498. referent_schema=referent_schema,
  499. initially=initially, match=match,
  500. **dialect_kw
  501. )
  502. return operations.invoke(op)
  503. @Operations.register_operation("create_check_constraint")
  504. @BatchOperations.register_operation(
  505. "create_check_constraint", "batch_create_check_constraint")
  506. @AddConstraintOp.register_add_constraint("check_constraint")
  507. @AddConstraintOp.register_add_constraint("column_check_constraint")
  508. class CreateCheckConstraintOp(AddConstraintOp):
  509. """Represent a create check constraint operation."""
  510. constraint_type = "check"
  511. def __init__(
  512. self, constraint_name, table_name,
  513. condition, schema=None, _orig_constraint=None, **kw):
  514. self.constraint_name = constraint_name
  515. self.table_name = table_name
  516. self.condition = condition
  517. self.schema = schema
  518. self._orig_constraint = _orig_constraint
  519. self.kw = kw
  520. @classmethod
  521. def from_constraint(cls, constraint):
  522. constraint_table = sqla_compat._table_for_constraint(constraint)
  523. return cls(
  524. constraint.name,
  525. constraint_table.name,
  526. constraint.sqltext,
  527. schema=constraint_table.schema,
  528. _orig_constraint=constraint
  529. )
  530. def to_constraint(self, migration_context=None):
  531. if self._orig_constraint is not None:
  532. return self._orig_constraint
  533. schema_obj = schemaobj.SchemaObjects(migration_context)
  534. return schema_obj.check_constraint(
  535. self.constraint_name, self.table_name,
  536. self.condition, schema=self.schema, **self.kw)
  537. @classmethod
  538. @util._with_legacy_names([
  539. ('name', 'constraint_name'),
  540. ('source', 'table_name')
  541. ])
  542. def create_check_constraint(
  543. cls, operations,
  544. constraint_name, table_name, condition,
  545. schema=None, **kw):
  546. """Issue a "create check constraint" instruction using the
  547. current migration context.
  548. e.g.::
  549. from alembic import op
  550. from sqlalchemy.sql import column, func
  551. op.create_check_constraint(
  552. "ck_user_name_len",
  553. "user",
  554. func.len(column('name')) > 5
  555. )
  556. CHECK constraints are usually against a SQL expression, so ad-hoc
  557. table metadata is usually needed. The function will convert the given
  558. arguments into a :class:`sqlalchemy.schema.CheckConstraint` bound
  559. to an anonymous table in order to emit the CREATE statement.
  560. :param name: Name of the check constraint. The name is necessary
  561. so that an ALTER statement can be emitted. For setups that
  562. use an automated naming scheme such as that described at
  563. :ref:`sqla:constraint_naming_conventions`,
  564. ``name`` here can be ``None``, as the event listener will
  565. apply the name to the constraint object when it is associated
  566. with the table.
  567. :param table_name: String name of the source table.
  568. :param condition: SQL expression that's the condition of the
  569. constraint. Can be a string or SQLAlchemy expression language
  570. structure.
  571. :param deferrable: optional bool. If set, emit DEFERRABLE or
  572. NOT DEFERRABLE when issuing DDL for this constraint.
  573. :param initially: optional string. If set, emit INITIALLY <value>
  574. when issuing DDL for this constraint.
  575. :param schema: Optional schema name to operate within. To control
  576. quoting of the schema outside of the default behavior, use
  577. the SQLAlchemy construct
  578. :class:`~sqlalchemy.sql.elements.quoted_name`.
  579. .. versionadded:: 0.7.0 'schema' can now accept a
  580. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  581. .. versionchanged:: 0.8.0 The following positional argument names
  582. have been changed:
  583. * name -> constraint_name
  584. * source -> table_name
  585. """
  586. op = cls(constraint_name, table_name, condition, schema=schema, **kw)
  587. return operations.invoke(op)
  588. @classmethod
  589. @util._with_legacy_names([('name', 'constraint_name')])
  590. def batch_create_check_constraint(
  591. cls, operations, constraint_name, condition, **kw):
  592. """Issue a "create check constraint" instruction using the
  593. current batch migration context.
  594. The batch form of this call omits the ``source`` and ``schema``
  595. arguments from the call.
  596. .. seealso::
  597. :meth:`.Operations.create_check_constraint`
  598. .. versionchanged:: 0.8.0 The following positional argument names
  599. have been changed:
  600. * name -> constraint_name
  601. """
  602. op = cls(
  603. constraint_name, operations.impl.table_name,
  604. condition, schema=operations.impl.schema, **kw)
  605. return operations.invoke(op)
  606. @Operations.register_operation("create_index")
  607. @BatchOperations.register_operation("create_index", "batch_create_index")
  608. class CreateIndexOp(MigrateOperation):
  609. """Represent a create index operation."""
  610. def __init__(
  611. self, index_name, table_name, columns, schema=None,
  612. unique=False, _orig_index=None, **kw):
  613. self.index_name = index_name
  614. self.table_name = table_name
  615. self.columns = columns
  616. self.schema = schema
  617. self.unique = unique
  618. self.kw = kw
  619. self._orig_index = _orig_index
  620. def reverse(self):
  621. return DropIndexOp.from_index(self.to_index())
  622. def to_diff_tuple(self):
  623. return ("add_index", self.to_index())
  624. @classmethod
  625. def from_index(cls, index):
  626. return cls(
  627. index.name,
  628. index.table.name,
  629. sqla_compat._get_index_expressions(index),
  630. schema=index.table.schema,
  631. unique=index.unique,
  632. _orig_index=index,
  633. **index.kwargs
  634. )
  635. def to_index(self, migration_context=None):
  636. if self._orig_index:
  637. return self._orig_index
  638. schema_obj = schemaobj.SchemaObjects(migration_context)
  639. return schema_obj.index(
  640. self.index_name, self.table_name, self.columns, schema=self.schema,
  641. unique=self.unique, **self.kw)
  642. @classmethod
  643. @util._with_legacy_names([('name', 'index_name')])
  644. def create_index(
  645. cls, operations,
  646. index_name, table_name, columns, schema=None,
  647. unique=False, **kw):
  648. """Issue a "create index" instruction using the current
  649. migration context.
  650. e.g.::
  651. from alembic import op
  652. op.create_index('ik_test', 't1', ['foo', 'bar'])
  653. Functional indexes can be produced by using the
  654. :func:`sqlalchemy.sql.expression.text` construct::
  655. from alembic import op
  656. from sqlalchemy import text
  657. op.create_index('ik_test', 't1', [text('lower(foo)')])
  658. .. versionadded:: 0.6.7 support for making use of the
  659. :func:`~sqlalchemy.sql.expression.text` construct in
  660. conjunction with
  661. :meth:`.Operations.create_index` in
  662. order to produce functional expressions within CREATE INDEX.
  663. :param index_name: name of the index.
  664. :param table_name: name of the owning table.
  665. :param columns: a list consisting of string column names and/or
  666. :func:`~sqlalchemy.sql.expression.text` constructs.
  667. :param schema: Optional schema name to operate within. To control
  668. quoting of the schema outside of the default behavior, use
  669. the SQLAlchemy construct
  670. :class:`~sqlalchemy.sql.elements.quoted_name`.
  671. .. versionadded:: 0.7.0 'schema' can now accept a
  672. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  673. :param unique: If True, create a unique index.
  674. :param quote:
  675. Force quoting of this column's name on or off, corresponding
  676. to ``True`` or ``False``. When left at its default
  677. of ``None``, the column identifier will be quoted according to
  678. whether the name is case sensitive (identifiers with at least one
  679. upper case character are treated as case sensitive), or if it's a
  680. reserved word. This flag is only needed to force quoting of a
  681. reserved word which is not known by the SQLAlchemy dialect.
  682. :param \**kw: Additional keyword arguments not mentioned above are
  683. dialect specific, and passed in the form
  684. ``<dialectname>_<argname>``.
  685. See the documentation regarding an individual dialect at
  686. :ref:`dialect_toplevel` for detail on documented arguments.
  687. .. versionchanged:: 0.8.0 The following positional argument names
  688. have been changed:
  689. * name -> index_name
  690. """
  691. op = cls(
  692. index_name, table_name, columns, schema=schema,
  693. unique=unique, **kw
  694. )
  695. return operations.invoke(op)
  696. @classmethod
  697. def batch_create_index(cls, operations, index_name, columns, **kw):
  698. """Issue a "create index" instruction using the
  699. current batch migration context.
  700. .. seealso::
  701. :meth:`.Operations.create_index`
  702. """
  703. op = cls(
  704. index_name, operations.impl.table_name, columns,
  705. schema=operations.impl.schema, **kw
  706. )
  707. return operations.invoke(op)
  708. @Operations.register_operation("drop_index")
  709. @BatchOperations.register_operation("drop_index", "batch_drop_index")
  710. class DropIndexOp(MigrateOperation):
  711. """Represent a drop index operation."""
  712. def __init__(
  713. self, index_name, table_name=None, schema=None, _orig_index=None):
  714. self.index_name = index_name
  715. self.table_name = table_name
  716. self.schema = schema
  717. self._orig_index = _orig_index
  718. def to_diff_tuple(self):
  719. return ("remove_index", self.to_index())
  720. def reverse(self):
  721. if self._orig_index is None:
  722. raise ValueError(
  723. "operation is not reversible; "
  724. "original index is not present")
  725. return CreateIndexOp.from_index(self._orig_index)
  726. @classmethod
  727. def from_index(cls, index):
  728. return cls(
  729. index.name,
  730. index.table.name,
  731. schema=index.table.schema,
  732. _orig_index=index
  733. )
  734. def to_index(self, migration_context=None):
  735. if self._orig_index is not None:
  736. return self._orig_index
  737. schema_obj = schemaobj.SchemaObjects(migration_context)
  738. # need a dummy column name here since SQLAlchemy
  739. # 0.7.6 and further raises on Index with no columns
  740. return schema_obj.index(
  741. self.index_name, self.table_name, ['x'], schema=self.schema)
  742. @classmethod
  743. @util._with_legacy_names([
  744. ('name', 'index_name'),
  745. ('tablename', 'table_name')
  746. ])
  747. def drop_index(cls, operations, index_name, table_name=None, schema=None):
  748. """Issue a "drop index" instruction using the current
  749. migration context.
  750. e.g.::
  751. drop_index("accounts")
  752. :param index_name: name of the index.
  753. :param table_name: name of the owning table. Some
  754. backends such as Microsoft SQL Server require this.
  755. :param schema: Optional schema name to operate within. To control
  756. quoting of the schema outside of the default behavior, use
  757. the SQLAlchemy construct
  758. :class:`~sqlalchemy.sql.elements.quoted_name`.
  759. .. versionadded:: 0.7.0 'schema' can now accept a
  760. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  761. .. versionchanged:: 0.8.0 The following positional argument names
  762. have been changed:
  763. * name -> index_name
  764. """
  765. op = cls(index_name, table_name=table_name, schema=schema)
  766. return operations.invoke(op)
  767. @classmethod
  768. @util._with_legacy_names([('name', 'index_name')])
  769. def batch_drop_index(cls, operations, index_name, **kw):
  770. """Issue a "drop index" instruction using the
  771. current batch migration context.
  772. .. seealso::
  773. :meth:`.Operations.drop_index`
  774. .. versionchanged:: 0.8.0 The following positional argument names
  775. have been changed:
  776. * name -> index_name
  777. """
  778. op = cls(
  779. index_name, table_name=operations.impl.table_name,
  780. schema=operations.impl.schema
  781. )
  782. return operations.invoke(op)
  783. @Operations.register_operation("create_table")
  784. class CreateTableOp(MigrateOperation):
  785. """Represent a create table operation."""
  786. def __init__(
  787. self, table_name, columns, schema=None, _orig_table=None, **kw):
  788. self.table_name = table_name
  789. self.columns = columns
  790. self.schema = schema
  791. self.kw = kw
  792. self._orig_table = _orig_table
  793. def reverse(self):
  794. return DropTableOp.from_table(self.to_table())
  795. def to_diff_tuple(self):
  796. return ("add_table", self.to_table())
  797. @classmethod
  798. def from_table(cls, table):
  799. return cls(
  800. table.name,
  801. list(table.c) + list(table.constraints),
  802. schema=table.schema,
  803. _orig_table=table,
  804. **table.kwargs
  805. )
  806. def to_table(self, migration_context=None):
  807. if self._orig_table is not None:
  808. return self._orig_table
  809. schema_obj = schemaobj.SchemaObjects(migration_context)
  810. return schema_obj.table(
  811. self.table_name, *self.columns, schema=self.schema, **self.kw
  812. )
  813. @classmethod
  814. @util._with_legacy_names([('name', 'table_name')])
  815. def create_table(cls, operations, table_name, *columns, **kw):
  816. """Issue a "create table" instruction using the current migration
  817. context.
  818. This directive receives an argument list similar to that of the
  819. traditional :class:`sqlalchemy.schema.Table` construct, but without the
  820. metadata::
  821. from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column
  822. from alembic import op
  823. op.create_table(
  824. 'account',
  825. Column('id', INTEGER, primary_key=True),
  826. Column('name', VARCHAR(50), nullable=False),
  827. Column('description', NVARCHAR(200)),
  828. Column('timestamp', TIMESTAMP, server_default=func.now())
  829. )
  830. Note that :meth:`.create_table` accepts
  831. :class:`~sqlalchemy.schema.Column`
  832. constructs directly from the SQLAlchemy library. In particular,
  833. default values to be created on the database side are
  834. specified using the ``server_default`` parameter, and not
  835. ``default`` which only specifies Python-side defaults::
  836. from alembic import op
  837. from sqlalchemy import Column, TIMESTAMP, func
  838. # specify "DEFAULT NOW" along with the "timestamp" column
  839. op.create_table('account',
  840. Column('id', INTEGER, primary_key=True),
  841. Column('timestamp', TIMESTAMP, server_default=func.now())
  842. )
  843. The function also returns a newly created
  844. :class:`~sqlalchemy.schema.Table` object, corresponding to the table
  845. specification given, which is suitable for
  846. immediate SQL operations, in particular
  847. :meth:`.Operations.bulk_insert`::
  848. from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column
  849. from alembic import op
  850. account_table = op.create_table(
  851. 'account',
  852. Column('id', INTEGER, primary_key=True),
  853. Column('name', VARCHAR(50), nullable=False),
  854. Column('description', NVARCHAR(200)),
  855. Column('timestamp', TIMESTAMP, server_default=func.now())
  856. )
  857. op.bulk_insert(
  858. account_table,
  859. [
  860. {"name": "A1", "description": "account 1"},
  861. {"name": "A2", "description": "account 2"},
  862. ]
  863. )
  864. .. versionadded:: 0.7.0
  865. :param table_name: Name of the table
  866. :param \*columns: collection of :class:`~sqlalchemy.schema.Column`
  867. objects within
  868. the table, as well as optional :class:`~sqlalchemy.schema.Constraint`
  869. objects
  870. and :class:`~.sqlalchemy.schema.Index` objects.
  871. :param schema: Optional schema name to operate within. To control
  872. quoting of the schema outside of the default behavior, use
  873. the SQLAlchemy construct
  874. :class:`~sqlalchemy.sql.elements.quoted_name`.
  875. .. versionadded:: 0.7.0 'schema' can now accept a
  876. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  877. :param \**kw: Other keyword arguments are passed to the underlying
  878. :class:`sqlalchemy.schema.Table` object created for the command.
  879. :return: the :class:`~sqlalchemy.schema.Table` object corresponding
  880. to the parameters given.
  881. .. versionadded:: 0.7.0 - the :class:`~sqlalchemy.schema.Table`
  882. object is returned.
  883. .. versionchanged:: 0.8.0 The following positional argument names
  884. have been changed:
  885. * name -> table_name
  886. """
  887. op = cls(table_name, columns, **kw)
  888. return operations.invoke(op)
  889. @Operations.register_operation("drop_table")
  890. class DropTableOp(MigrateOperation):
  891. """Represent a drop table operation."""
  892. def __init__(
  893. self, table_name, schema=None, table_kw=None, _orig_table=None):
  894. self.table_name = table_name
  895. self.schema = schema
  896. self.table_kw = table_kw or {}
  897. self._orig_table = _orig_table
  898. def to_diff_tuple(self):
  899. return ("remove_table", self.to_table())
  900. def reverse(self):
  901. if self._orig_table is None:
  902. raise ValueError(
  903. "operation is not reversible; "
  904. "original table is not present")
  905. return CreateTableOp.from_table(self._orig_table)
  906. @classmethod
  907. def from_table(cls, table):
  908. return cls(table.name, schema=table.schema, _orig_table=table)
  909. def to_table(self, migration_context=None):
  910. if self._orig_table is not None:
  911. return self._orig_table
  912. schema_obj = schemaobj.SchemaObjects(migration_context)
  913. return schema_obj.table(
  914. self.table_name,
  915. schema=self.schema,
  916. **self.table_kw)
  917. @classmethod
  918. @util._with_legacy_names([('name', 'table_name')])
  919. def drop_table(cls, operations, table_name, schema=None, **kw):
  920. """Issue a "drop table" instruction using the current
  921. migration context.
  922. e.g.::
  923. drop_table("accounts")
  924. :param table_name: Name of the table
  925. :param schema: Optional schema name to operate within. To control
  926. quoting of the schema outside of the default behavior, use
  927. the SQLAlchemy construct
  928. :class:`~sqlalchemy.sql.elements.quoted_name`.
  929. .. versionadded:: 0.7.0 'schema' can now accept a
  930. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  931. :param \**kw: Other keyword arguments are passed to the underlying
  932. :class:`sqlalchemy.schema.Table` object created for the command.
  933. .. versionchanged:: 0.8.0 The following positional argument names
  934. have been changed:
  935. * name -> table_name
  936. """
  937. op = cls(table_name, schema=schema, table_kw=kw)
  938. operations.invoke(op)
  939. class AlterTableOp(MigrateOperation):
  940. """Represent an alter table operation."""
  941. def __init__(self, table_name, schema=None):
  942. self.table_name = table_name
  943. self.schema = schema
  944. @Operations.register_operation("rename_table")
  945. class RenameTableOp(AlterTableOp):
  946. """Represent a rename table operation."""
  947. def __init__(self, old_table_name, new_table_name, schema=None):
  948. super(RenameTableOp, self).__init__(old_table_name, schema=schema)
  949. self.new_table_name = new_table_name
  950. @classmethod
  951. def rename_table(
  952. cls, operations, old_table_name, new_table_name, schema=None):
  953. """Emit an ALTER TABLE to rename a table.
  954. :param old_table_name: old name.
  955. :param new_table_name: new name.
  956. :param schema: Optional schema name to operate within. To control
  957. quoting of the schema outside of the default behavior, use
  958. the SQLAlchemy construct
  959. :class:`~sqlalchemy.sql.elements.quoted_name`.
  960. .. versionadded:: 0.7.0 'schema' can now accept a
  961. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  962. """
  963. op = cls(old_table_name, new_table_name, schema=schema)
  964. return operations.invoke(op)
  965. @Operations.register_operation("alter_column")
  966. @BatchOperations.register_operation("alter_column", "batch_alter_column")
  967. class AlterColumnOp(AlterTableOp):
  968. """Represent an alter column operation."""
  969. def __init__(
  970. self, table_name, column_name, schema=None,
  971. existing_type=None,
  972. existing_server_default=False,
  973. existing_nullable=None,
  974. modify_nullable=None,
  975. modify_server_default=False,
  976. modify_name=None,
  977. modify_type=None,
  978. **kw
  979. ):
  980. super(AlterColumnOp, self).__init__(table_name, schema=schema)
  981. self.column_name = column_name
  982. self.existing_type = existing_type
  983. self.existing_server_default = existing_server_default
  984. self.existing_nullable = existing_nullable
  985. self.modify_nullable = modify_nullable
  986. self.modify_server_default = modify_server_default
  987. self.modify_name = modify_name
  988. self.modify_type = modify_type
  989. self.kw = kw
  990. def to_diff_tuple(self):
  991. col_diff = []
  992. schema, tname, cname = self.schema, self.table_name, self.column_name
  993. if self.modify_type is not None:
  994. col_diff.append(
  995. ("modify_type", schema, tname, cname,
  996. {
  997. "existing_nullable": self.existing_nullable,
  998. "existing_server_default": self.existing_server_default,
  999. },
  1000. self.existing_type,
  1001. self.modify_type)
  1002. )
  1003. if self.modify_nullable is not None:
  1004. col_diff.append(
  1005. ("modify_nullable", schema, tname, cname,
  1006. {
  1007. "existing_type": self.existing_type,
  1008. "existing_server_default": self.existing_server_default
  1009. },
  1010. self.existing_nullable,
  1011. self.modify_nullable)
  1012. )
  1013. if self.modify_server_default is not False:
  1014. col_diff.append(
  1015. ("modify_default", schema, tname, cname,
  1016. {
  1017. "existing_nullable": self.existing_nullable,
  1018. "existing_type": self.existing_type
  1019. },
  1020. self.existing_server_default,
  1021. self.modify_server_default)
  1022. )
  1023. return col_diff
  1024. def has_changes(self):
  1025. hc1 = self.modify_nullable is not None or \
  1026. self.modify_server_default is not False or \
  1027. self.modify_type is not None
  1028. if hc1:
  1029. return True
  1030. for kw in self.kw:
  1031. if kw.startswith('modify_'):
  1032. return True
  1033. else:
  1034. return False
  1035. def reverse(self):
  1036. kw = self.kw.copy()
  1037. kw['existing_type'] = self.existing_type
  1038. kw['existing_nullable'] = self.existing_nullable
  1039. kw['existing_server_default'] = self.existing_server_default
  1040. if self.modify_type is not None:
  1041. kw['modify_type'] = self.modify_type
  1042. if self.modify_nullable is not None:
  1043. kw['modify_nullable'] = self.modify_nullable
  1044. if self.modify_server_default is not False:
  1045. kw['modify_server_default'] = self.modify_server_default
  1046. # TODO: make this a little simpler
  1047. all_keys = set(m.group(1) for m in [
  1048. re.match(r'^(?:existing_|modify_)(.+)$', k)
  1049. for k in kw
  1050. ] if m)
  1051. for k in all_keys:
  1052. if 'modify_%s' % k in kw:
  1053. swap = kw['existing_%s' % k]
  1054. kw['existing_%s' % k] = kw['modify_%s' % k]
  1055. kw['modify_%s' % k] = swap
  1056. return self.__class__(
  1057. self.table_name, self.column_name, schema=self.schema,
  1058. **kw
  1059. )
  1060. @classmethod
  1061. @util._with_legacy_names([('name', 'new_column_name')])
  1062. def alter_column(
  1063. cls, operations, table_name, column_name,
  1064. nullable=None,
  1065. server_default=False,
  1066. new_column_name=None,
  1067. type_=None,
  1068. existing_type=None,
  1069. existing_server_default=False,
  1070. existing_nullable=None,
  1071. schema=None, **kw
  1072. ):
  1073. """Issue an "alter column" instruction using the
  1074. current migration context.
  1075. Generally, only that aspect of the column which
  1076. is being changed, i.e. name, type, nullability,
  1077. default, needs to be specified. Multiple changes
  1078. can also be specified at once and the backend should
  1079. "do the right thing", emitting each change either
  1080. separately or together as the backend allows.
  1081. MySQL has special requirements here, since MySQL
  1082. cannot ALTER a column without a full specification.
  1083. When producing MySQL-compatible migration files,
  1084. it is recommended that the ``existing_type``,
  1085. ``existing_server_default``, and ``existing_nullable``
  1086. parameters be present, if not being altered.
  1087. Type changes which are against the SQLAlchemy
  1088. "schema" types :class:`~sqlalchemy.types.Boolean`
  1089. and :class:`~sqlalchemy.types.Enum` may also
  1090. add or drop constraints which accompany those
  1091. types on backends that don't support them natively.
  1092. The ``existing_type`` argument is
  1093. used in this case to identify and remove a previous
  1094. constraint that was bound to the type object.
  1095. :param table_name: string name of the target table.
  1096. :param column_name: string name of the target column,
  1097. as it exists before the operation begins.
  1098. :param nullable: Optional; specify ``True`` or ``False``
  1099. to alter the column's nullability.
  1100. :param server_default: Optional; specify a string
  1101. SQL expression, :func:`~sqlalchemy.sql.expression.text`,
  1102. or :class:`~sqlalchemy.schema.DefaultClause` to indicate
  1103. an alteration to the column's default value.
  1104. Set to ``None`` to have the default removed.
  1105. :param new_column_name: Optional; specify a string name here to
  1106. indicate the new name within a column rename operation.
  1107. :param type_: Optional; a :class:`~sqlalchemy.types.TypeEngine`
  1108. type object to specify a change to the column's type.
  1109. For SQLAlchemy types that also indicate a constraint (i.e.
  1110. :class:`~sqlalchemy.types.Boolean`, :class:`~sqlalchemy.types.Enum`),
  1111. the constraint is also generated.
  1112. :param autoincrement: set the ``AUTO_INCREMENT`` flag of the column;
  1113. currently understood by the MySQL dialect.
  1114. :param existing_type: Optional; a
  1115. :class:`~sqlalchemy.types.TypeEngine`
  1116. type object to specify the previous type. This
  1117. is required for all MySQL column alter operations that
  1118. don't otherwise specify a new type, as well as for
  1119. when nullability is being changed on a SQL Server
  1120. column. It is also used if the type is a so-called
  1121. SQLlchemy "schema" type which may define a constraint (i.e.
  1122. :class:`~sqlalchemy.types.Boolean`,
  1123. :class:`~sqlalchemy.types.Enum`),
  1124. so that the constraint can be dropped.
  1125. :param existing_server_default: Optional; The existing
  1126. default value of the column. Required on MySQL if
  1127. an existing default is not being changed; else MySQL
  1128. removes the default.
  1129. :param existing_nullable: Optional; the existing nullability
  1130. of the column. Required on MySQL if the existing nullability
  1131. is not being changed; else MySQL sets this to NULL.
  1132. :param existing_autoincrement: Optional; the existing autoincrement
  1133. of the column. Used for MySQL's system of altering a column
  1134. that specifies ``AUTO_INCREMENT``.
  1135. :param schema: Optional schema name to operate within. To control
  1136. quoting of the schema outside of the default behavior, use
  1137. the SQLAlchemy construct
  1138. :class:`~sqlalchemy.sql.elements.quoted_name`.
  1139. .. versionadded:: 0.7.0 'schema' can now accept a
  1140. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  1141. :param postgresql_using: String argument which will indicate a
  1142. SQL expression to render within the Postgresql-specific USING clause
  1143. within ALTER COLUMN. This string is taken directly as raw SQL which
  1144. must explicitly include any necessary quoting or escaping of tokens
  1145. within the expression.
  1146. .. versionadded:: 0.8.8
  1147. """
  1148. alt = cls(
  1149. table_name, column_name, schema=schema,
  1150. existing_type=existing_type,
  1151. existing_server_default=existing_server_default,
  1152. existing_nullable=existing_nullable,
  1153. modify_name=new_column_name,
  1154. modify_type=type_,
  1155. modify_server_default=server_default,
  1156. modify_nullable=nullable,
  1157. **kw
  1158. )
  1159. return operations.invoke(alt)
  1160. @classmethod
  1161. def batch_alter_column(
  1162. cls, operations, column_name,
  1163. nullable=None,
  1164. server_default=False,
  1165. new_column_name=None,
  1166. type_=None,
  1167. existing_type=None,
  1168. existing_server_default=False,
  1169. existing_nullable=None,
  1170. **kw
  1171. ):
  1172. """Issue an "alter column" instruction using the current
  1173. batch migration context.
  1174. .. seealso::
  1175. :meth:`.Operations.alter_column`
  1176. """
  1177. alt = cls(
  1178. operations.impl.table_name, column_name,
  1179. schema=operations.impl.schema,
  1180. existing_type=existing_type,
  1181. existing_server_default=existing_server_default,
  1182. existing_nullable=existing_nullable,
  1183. modify_name=new_column_name,
  1184. modify_type=type_,
  1185. modify_server_default=server_default,
  1186. modify_nullable=nullable,
  1187. **kw
  1188. )
  1189. return operations.invoke(alt)
  1190. @Operations.register_operation("add_column")
  1191. @BatchOperations.register_operation("add_column", "batch_add_column")
  1192. class AddColumnOp(AlterTableOp):
  1193. """Represent an add column operation."""
  1194. def __init__(self, table_name, column, schema=None):
  1195. super(AddColumnOp, self).__init__(table_name, schema=schema)
  1196. self.column = column
  1197. def reverse(self):
  1198. return DropColumnOp.from_column_and_tablename(
  1199. self.schema, self.table_name, self.column)
  1200. def to_diff_tuple(self):
  1201. return ("add_column", self.schema, self.table_name, self.column)
  1202. def to_column(self):
  1203. return self.column
  1204. @classmethod
  1205. def from_column(cls, col):
  1206. return cls(col.table.name, col, schema=col.table.schema)
  1207. @classmethod
  1208. def from_column_and_tablename(cls, schema, tname, col):
  1209. return cls(tname, col, schema=schema)
  1210. @classmethod
  1211. def add_column(cls, operations, table_name, column, schema=None):
  1212. """Issue an "add column" instruction using the current
  1213. migration context.
  1214. e.g.::
  1215. from alembic import op
  1216. from sqlalchemy import Column, String
  1217. op.add_column('organization',
  1218. Column('name', String())
  1219. )
  1220. The provided :class:`~sqlalchemy.schema.Column` object can also
  1221. specify a :class:`~sqlalchemy.schema.ForeignKey`, referencing
  1222. a remote table name. Alembic will automatically generate a stub
  1223. "referenced" table and emit a second ALTER statement in order
  1224. to add the constraint separately::
  1225. from alembic import op
  1226. from sqlalchemy import Column, INTEGER, ForeignKey
  1227. op.add_column('organization',
  1228. Column('account_id', INTEGER, ForeignKey('accounts.id'))
  1229. )
  1230. Note that this statement uses the :class:`~sqlalchemy.schema.Column`
  1231. construct as is from the SQLAlchemy library. In particular,
  1232. default values to be created on the database side are
  1233. specified using the ``server_default`` parameter, and not
  1234. ``default`` which only specifies Python-side defaults::
  1235. from alembic import op
  1236. from sqlalchemy import Column, TIMESTAMP, func
  1237. # specify "DEFAULT NOW" along with the column add
  1238. op.add_column('account',
  1239. Column('timestamp', TIMESTAMP, server_default=func.now())
  1240. )
  1241. :param table_name: String name of the parent table.
  1242. :param column: a :class:`sqlalchemy.schema.Column` object
  1243. representing the new column.
  1244. :param schema: Optional schema name to operate within. To control
  1245. quoting of the schema outside of the default behavior, use
  1246. the SQLAlchemy construct
  1247. :class:`~sqlalchemy.sql.elements.quoted_name`.
  1248. .. versionadded:: 0.7.0 'schema' can now accept a
  1249. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  1250. """
  1251. op = cls(table_name, column, schema=schema)
  1252. return operations.invoke(op)
  1253. @classmethod
  1254. def batch_add_column(cls, operations, column):
  1255. """Issue an "add column" instruction using the current
  1256. batch migration context.
  1257. .. seealso::
  1258. :meth:`.Operations.add_column`
  1259. """
  1260. op = cls(
  1261. operations.impl.table_name, column,
  1262. schema=operations.impl.schema
  1263. )
  1264. return operations.invoke(op)
  1265. @Operations.register_operation("drop_column")
  1266. @BatchOperations.register_operation("drop_column", "batch_drop_column")
  1267. class DropColumnOp(AlterTableOp):
  1268. """Represent a drop column operation."""
  1269. def __init__(
  1270. self, table_name, column_name, schema=None,
  1271. _orig_column=None, **kw):
  1272. super(DropColumnOp, self).__init__(table_name, schema=schema)
  1273. self.column_name = column_name
  1274. self.kw = kw
  1275. self._orig_column = _orig_column
  1276. def to_diff_tuple(self):
  1277. return (
  1278. "remove_column", self.schema, self.table_name, self.to_column())
  1279. def reverse(self):
  1280. if self._orig_column is None:
  1281. raise ValueError(
  1282. "operation is not reversible; "
  1283. "original column is not present")
  1284. return AddColumnOp.from_column_and_tablename(
  1285. self.schema, self.table_name, self._orig_column)
  1286. @classmethod
  1287. def from_column_and_tablename(cls, schema, tname, col):
  1288. return cls(tname, col.name, schema=schema, _orig_column=col)
  1289. def to_column(self, migration_context=None):
  1290. if self._orig_column is not None:
  1291. return self._orig_column
  1292. schema_obj = schemaobj.SchemaObjects(migration_context)
  1293. return schema_obj.column(self.column_name, NULLTYPE)
  1294. @classmethod
  1295. def drop_column(
  1296. cls, operations, table_name, column_name, schema=None, **kw):
  1297. """Issue a "drop column" instruction using the current
  1298. migration context.
  1299. e.g.::
  1300. drop_column('organization', 'account_id')
  1301. :param table_name: name of table
  1302. :param column_name: name of column
  1303. :param schema: Optional schema name to operate within. To control
  1304. quoting of the schema outside of the default behavior, use
  1305. the SQLAlchemy construct
  1306. :class:`~sqlalchemy.sql.elements.quoted_name`.
  1307. .. versionadded:: 0.7.0 'schema' can now accept a
  1308. :class:`~sqlalchemy.sql.elements.quoted_name` construct.
  1309. :param mssql_drop_check: Optional boolean. When ``True``, on
  1310. Microsoft SQL Server only, first
  1311. drop the CHECK constraint on the column using a
  1312. SQL-script-compatible
  1313. block that selects into a @variable from sys.check_constraints,
  1314. then exec's a separate DROP CONSTRAINT for that constraint.
  1315. :param mssql_drop_default: Optional boolean. When ``True``, on
  1316. Microsoft SQL Server only, first
  1317. drop the DEFAULT constraint on the column using a
  1318. SQL-script-compatible
  1319. block that selects into a @variable from sys.default_constraints,
  1320. then exec's a separate DROP CONSTRAINT for that default.
  1321. :param mssql_drop_foreign_key: Optional boolean. When ``True``, on
  1322. Microsoft SQL Server only, first
  1323. drop a single FOREIGN KEY constraint on the column using a
  1324. SQL-script-compatible
  1325. block that selects into a @variable from
  1326. sys.foreign_keys/sys.foreign_key_columns,
  1327. then exec's a separate DROP CONSTRAINT for that default. Only
  1328. works if the column has exactly one FK constraint which refers to
  1329. it, at the moment.
  1330. .. versionadded:: 0.6.2
  1331. """
  1332. op = cls(table_name, column_name, schema=schema, **kw)
  1333. return operations.invoke(op)
  1334. @classmethod
  1335. def batch_drop_column(cls, operations, column_name, **kw):
  1336. """Issue a "drop column" instruction using the current
  1337. batch migration context.
  1338. .. seealso::
  1339. :meth:`.Operations.drop_column`
  1340. """
  1341. op = cls(
  1342. operations.impl.table_name, column_name,
  1343. schema=operations.impl.schema, **kw)
  1344. return operations.invoke(op)
  1345. @Operations.register_operation("bulk_insert")
  1346. class BulkInsertOp(MigrateOperation):
  1347. """Represent a bulk insert operation."""
  1348. def __init__(self, table, rows, multiinsert=True):
  1349. self.table = table
  1350. self.rows = rows
  1351. self.multiinsert = multiinsert
  1352. @classmethod
  1353. def bulk_insert(cls, operations, table, rows, multiinsert=True):
  1354. """Issue a "bulk insert" operation using the current
  1355. migration context.
  1356. This provides a means of representing an INSERT of multiple rows
  1357. which works equally well in the context of executing on a live
  1358. connection as well as that of generating a SQL script. In the
  1359. case of a SQL script, the values are rendered inline into the
  1360. statement.
  1361. e.g.::
  1362. from alembic import op
  1363. from datetime import date
  1364. from sqlalchemy.sql import table, column
  1365. from sqlalchemy import String, Integer, Date
  1366. # Create an ad-hoc table to use for the insert statement.
  1367. accounts_table = table('account',
  1368. column('id', Integer),
  1369. column('name', String),
  1370. column('create_date', Date)
  1371. )
  1372. op.bulk_insert(accounts_table,
  1373. [
  1374. {'id':1, 'name':'John Smith',
  1375. 'create_date':date(2010, 10, 5)},
  1376. {'id':2, 'name':'Ed Williams',
  1377. 'create_date':date(2007, 5, 27)},
  1378. {'id':3, 'name':'Wendy Jones',
  1379. 'create_date':date(2008, 8, 15)},
  1380. ]
  1381. )
  1382. When using --sql mode, some datatypes may not render inline
  1383. automatically, such as dates and other special types. When this
  1384. issue is present, :meth:`.Operations.inline_literal` may be used::
  1385. op.bulk_insert(accounts_table,
  1386. [
  1387. {'id':1, 'name':'John Smith',
  1388. 'create_date':op.inline_literal("2010-10-05")},
  1389. {'id':2, 'name':'Ed Williams',
  1390. 'create_date':op.inline_literal("2007-05-27")},
  1391. {'id':3, 'name':'Wendy Jones',
  1392. 'create_date':op.inline_literal("2008-08-15")},
  1393. ],
  1394. multiinsert=False
  1395. )
  1396. When using :meth:`.Operations.inline_literal` in conjunction with
  1397. :meth:`.Operations.bulk_insert`, in order for the statement to work
  1398. in "online" (e.g. non --sql) mode, the
  1399. :paramref:`~.Operations.bulk_insert.multiinsert`
  1400. flag should be set to ``False``, which will have the effect of
  1401. individual INSERT statements being emitted to the database, each
  1402. with a distinct VALUES clause, so that the "inline" values can
  1403. still be rendered, rather than attempting to pass the values
  1404. as bound parameters.
  1405. .. versionadded:: 0.6.4 :meth:`.Operations.inline_literal` can now
  1406. be used with :meth:`.Operations.bulk_insert`, and the
  1407. :paramref:`~.Operations.bulk_insert.multiinsert` flag has
  1408. been added to assist in this usage when running in "online"
  1409. mode.
  1410. :param table: a table object which represents the target of the INSERT.
  1411. :param rows: a list of dictionaries indicating rows.
  1412. :param multiinsert: when at its default of True and --sql mode is not
  1413. enabled, the INSERT statement will be executed using
  1414. "executemany()" style, where all elements in the list of
  1415. dictionaries are passed as bound parameters in a single
  1416. list. Setting this to False results in individual INSERT
  1417. statements being emitted per parameter set, and is needed
  1418. in those cases where non-literal values are present in the
  1419. parameter sets.
  1420. .. versionadded:: 0.6.4
  1421. """
  1422. op = cls(table, rows, multiinsert=multiinsert)
  1423. operations.invoke(op)
  1424. @Operations.register_operation("execute")
  1425. class ExecuteSQLOp(MigrateOperation):
  1426. """Represent an execute SQL operation."""
  1427. def __init__(self, sqltext, execution_options=None):
  1428. self.sqltext = sqltext
  1429. self.execution_options = execution_options
  1430. @classmethod
  1431. def execute(cls, operations, sqltext, execution_options=None):
  1432. """Execute the given SQL using the current migration context.
  1433. In a SQL script context, the statement is emitted directly to the
  1434. output stream. There is *no* return result, however, as this
  1435. function is oriented towards generating a change script
  1436. that can run in "offline" mode. For full interaction
  1437. with a connected database, use the "bind" available
  1438. from the context::
  1439. from alembic import op
  1440. connection = op.get_bind()
  1441. Also note that any parameterized statement here *will not work*
  1442. in offline mode - INSERT, UPDATE and DELETE statements which refer
  1443. to literal values would need to render
  1444. inline expressions. For simple use cases, the
  1445. :meth:`.inline_literal` function can be used for **rudimentary**
  1446. quoting of string values. For "bulk" inserts, consider using
  1447. :meth:`.bulk_insert`.
  1448. For example, to emit an UPDATE statement which is equally
  1449. compatible with both online and offline mode::
  1450. from sqlalchemy.sql import table, column
  1451. from sqlalchemy import String
  1452. from alembic import op
  1453. account = table('account',
  1454. column('name', String)
  1455. )
  1456. op.execute(
  1457. account.update().\\
  1458. where(account.c.name==op.inline_literal('account 1')).\\
  1459. values({'name':op.inline_literal('account 2')})
  1460. )
  1461. Note above we also used the SQLAlchemy
  1462. :func:`sqlalchemy.sql.expression.table`
  1463. and :func:`sqlalchemy.sql.expression.column` constructs to
  1464. make a brief, ad-hoc table construct just for our UPDATE
  1465. statement. A full :class:`~sqlalchemy.schema.Table` construct
  1466. of course works perfectly fine as well, though note it's a
  1467. recommended practice to at least ensure the definition of a
  1468. table is self-contained within the migration script, rather
  1469. than imported from a module that may break compatibility with
  1470. older migrations.
  1471. :param sql: Any legal SQLAlchemy expression, including:
  1472. * a string
  1473. * a :func:`sqlalchemy.sql.expression.text` construct.
  1474. * a :func:`sqlalchemy.sql.expression.insert` construct.
  1475. * a :func:`sqlalchemy.sql.expression.update`,
  1476. :func:`sqlalchemy.sql.expression.insert`,
  1477. or :func:`sqlalchemy.sql.expression.delete` construct.
  1478. * Pretty much anything that's "executable" as described
  1479. in :ref:`sqlexpression_toplevel`.
  1480. :param execution_options: Optional dictionary of
  1481. execution options, will be passed to
  1482. :meth:`sqlalchemy.engine.Connection.execution_options`.
  1483. """
  1484. op = cls(sqltext, execution_options=execution_options)
  1485. return operations.invoke(op)
  1486. class OpContainer(MigrateOperation):
  1487. """Represent a sequence of operations operation."""
  1488. def __init__(self, ops=()):
  1489. self.ops = ops
  1490. def is_empty(self):
  1491. return not self.ops
  1492. def as_diffs(self):
  1493. return list(OpContainer._ops_as_diffs(self))
  1494. @classmethod
  1495. def _ops_as_diffs(cls, migrations):
  1496. for op in migrations.ops:
  1497. if hasattr(op, 'ops'):
  1498. for sub_op in cls._ops_as_diffs(op):
  1499. yield sub_op
  1500. else:
  1501. yield op.to_diff_tuple()
  1502. class ModifyTableOps(OpContainer):
  1503. """Contains a sequence of operations that all apply to a single Table."""
  1504. def __init__(self, table_name, ops, schema=None):
  1505. super(ModifyTableOps, self).__init__(ops)
  1506. self.table_name = table_name
  1507. self.schema = schema
  1508. def reverse(self):
  1509. return ModifyTableOps(
  1510. self.table_name,
  1511. ops=list(reversed(
  1512. [op.reverse() for op in self.ops]
  1513. )),
  1514. schema=self.schema
  1515. )
  1516. class UpgradeOps(OpContainer):
  1517. """contains a sequence of operations that would apply to the
  1518. 'upgrade' stream of a script.
  1519. .. seealso::
  1520. :ref:`customizing_revision`
  1521. """
  1522. def __init__(self, ops=(), upgrade_token="upgrades"):
  1523. super(UpgradeOps, self).__init__(ops=ops)
  1524. self.upgrade_token = upgrade_token
  1525. def reverse_into(self, downgrade_ops):
  1526. downgrade_ops.ops[:] = list(reversed(
  1527. [op.reverse() for op in self.ops]
  1528. ))
  1529. return downgrade_ops
  1530. def reverse(self):
  1531. return self.reverse_into(DowngradeOps(ops=[]))
  1532. class DowngradeOps(OpContainer):
  1533. """contains a sequence of operations that would apply to the
  1534. 'downgrade' stream of a script.
  1535. .. seealso::
  1536. :ref:`customizing_revision`
  1537. """
  1538. def __init__(self, ops=(), downgrade_token="downgrades"):
  1539. super(DowngradeOps, self).__init__(ops=ops)
  1540. self.downgrade_token = downgrade_token
  1541. def reverse(self):
  1542. return UpgradeOps(
  1543. ops=list(reversed(
  1544. [op.reverse() for op in self.ops]
  1545. ))
  1546. )
  1547. class MigrationScript(MigrateOperation):
  1548. """represents a migration script.
  1549. E.g. when autogenerate encounters this object, this corresponds to the
  1550. production of an actual script file.
  1551. A normal :class:`.MigrationScript` object would contain a single
  1552. :class:`.UpgradeOps` and a single :class:`.DowngradeOps` directive.
  1553. These are accessible via the ``.upgrade_ops`` and ``.downgrade_ops``
  1554. attributes.
  1555. In the case of an autogenerate operation that runs multiple times,
  1556. such as the multiple database example in the "multidb" template,
  1557. the ``.upgrade_ops`` and ``.downgrade_ops`` attributes are disabled,
  1558. and instead these objects should be accessed via the ``.upgrade_ops_list``
  1559. and ``.downgrade_ops_list`` list-based attributes. These latter
  1560. attributes are always available at the very least as single-element lists.
  1561. .. versionchanged:: 0.8.1 the ``.upgrade_ops`` and ``.downgrade_ops``
  1562. attributes should be accessed via the ``.upgrade_ops_list``
  1563. and ``.downgrade_ops_list`` attributes if multiple autogenerate
  1564. passes proceed on the same :class:`.MigrationScript` object.
  1565. .. seealso::
  1566. :ref:`customizing_revision`
  1567. """
  1568. def __init__(
  1569. self, rev_id, upgrade_ops, downgrade_ops,
  1570. message=None,
  1571. imports=set(), head=None, splice=None,
  1572. branch_label=None, version_path=None, depends_on=None):
  1573. self.rev_id = rev_id
  1574. self.message = message
  1575. self.imports = imports
  1576. self.head = head
  1577. self.splice = splice
  1578. self.branch_label = branch_label
  1579. self.version_path = version_path
  1580. self.depends_on = depends_on
  1581. self.upgrade_ops = upgrade_ops
  1582. self.downgrade_ops = downgrade_ops
  1583. @property
  1584. def upgrade_ops(self):
  1585. """An instance of :class:`.UpgradeOps`.
  1586. .. seealso::
  1587. :attr:`.MigrationScript.upgrade_ops_list`
  1588. """
  1589. if len(self._upgrade_ops) > 1:
  1590. raise ValueError(
  1591. "This MigrationScript instance has a multiple-entry "
  1592. "list for UpgradeOps; please use the "
  1593. "upgrade_ops_list attribute.")
  1594. elif not self._upgrade_ops:
  1595. return None
  1596. else:
  1597. return self._upgrade_ops[0]
  1598. @upgrade_ops.setter
  1599. def upgrade_ops(self, upgrade_ops):
  1600. self._upgrade_ops = util.to_list(upgrade_ops)
  1601. for elem in self._upgrade_ops:
  1602. assert isinstance(elem, UpgradeOps)
  1603. @property
  1604. def downgrade_ops(self):
  1605. """An instance of :class:`.DowngradeOps`.
  1606. .. seealso::
  1607. :attr:`.MigrationScript.downgrade_ops_list`
  1608. """
  1609. if len(self._downgrade_ops) > 1:
  1610. raise ValueError(
  1611. "This MigrationScript instance has a multiple-entry "
  1612. "list for DowngradeOps; please use the "
  1613. "downgrade_ops_list attribute.")
  1614. elif not self._downgrade_ops:
  1615. return None
  1616. else:
  1617. return self._downgrade_ops[0]
  1618. @downgrade_ops.setter
  1619. def downgrade_ops(self, downgrade_ops):
  1620. self._downgrade_ops = util.to_list(downgrade_ops)
  1621. for elem in self._downgrade_ops:
  1622. assert isinstance(elem, DowngradeOps)
  1623. @property
  1624. def upgrade_ops_list(self):
  1625. """A list of :class:`.UpgradeOps` instances.
  1626. This is used in place of the :attr:`.MigrationScript.upgrade_ops`
  1627. attribute when dealing with a revision operation that does
  1628. multiple autogenerate passes.
  1629. .. versionadded:: 0.8.1
  1630. """
  1631. return self._upgrade_ops
  1632. @property
  1633. def downgrade_ops_list(self):
  1634. """A list of :class:`.DowngradeOps` instances.
  1635. This is used in place of the :attr:`.MigrationScript.downgrade_ops`
  1636. attribute when dealing with a revision operation that does
  1637. multiple autogenerate passes.
  1638. .. versionadded:: 0.8.1
  1639. """
  1640. return self._downgrade_ops