]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Render `if_not_exists` option for CreateTableOp, CreateIndexOp, DropTableOp and...
authorLouis-Amaury Chaib <louisamaury.chaib@partoo.fr>
Mon, 23 Sep 2024 12:23:02 +0000 (08:23 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 23 Sep 2024 12:34:07 +0000 (08:34 -0400)
Render ``if_exists`` and ``if_not_exists`` parameters in
:class:`.CreateTableOp`, :class:`.CreateIndexOp`, :class:`.DropTableOp` and
:class:`.DropIndexOp` in an autogenerate context.  While Alembic does not
set these parameters during an autogenerate run, they can be enabled using
a custom :class:`.Rewriter` in the ``env.py`` file, where they will now be
part of the rendered Python code in revision files.  Pull request courtesy
of Louis-Amaury Chaib (@lachaib).

Closes: #1446
Pull-request: https://github.com/sqlalchemy/alembic/pull/1446
Pull-request-sha: 90c9735767af1cf3ba7e40e71dfa0fb30efc1ee8

Change-Id: I6b0a5ffaf7e2d1a0a1e1f1e80ed0ee168ae2bd09

alembic/autogenerate/render.py
docs/build/unreleased/1446.rst [new file with mode: 0644]
tests/test_autogen_render.py

index 61d56acfed416a77e0dbf333ef36cb88af74c894..38bdbfca26e3d87623d9807e884498e9f7e3fcff 100644 (file)
@@ -279,6 +279,9 @@ def _add_table(autogen_context: AutogenContext, op: ops.CreateTableOp) -> str:
         prefixes = ", ".join("'%s'" % p for p in table._prefixes)
         text += ",\nprefixes=[%s]" % prefixes
 
+    if op.if_not_exists is not None:
+        text += ",\nif_not_exists=%r" % bool(op.if_not_exists)
+
     text += "\n)"
     return text
 
@@ -291,6 +294,10 @@ def _drop_table(autogen_context: AutogenContext, op: ops.DropTableOp) -> str:
     }
     if op.schema:
         text += ", schema=%r" % _ident(op.schema)
+
+    if op.if_exists is not None:
+        text += ", if_exists=%r" % bool(op.if_exists)
+
     text += ")"
     return text
 
@@ -324,6 +331,8 @@ def _add_index(autogen_context: AutogenContext, op: ops.CreateIndexOp) -> str:
     assert index.table is not None
 
     opts = _render_dialect_kwargs_items(autogen_context, index)
+    if op.if_not_exists is not None:
+        opts.append("if_not_exists=%r" % bool(op.if_not_exists))
     text = tmpl % {
         "prefix": _alembic_autogenerate_prefix(autogen_context),
         "name": _render_gen_name(autogen_context, index.name),
@@ -356,6 +365,8 @@ def _drop_index(autogen_context: AutogenContext, op: ops.DropIndexOp) -> str:
             "table_name=%(table_name)r%(schema)s%(kwargs)s)"
         )
     opts = _render_dialect_kwargs_items(autogen_context, index)
+    if op.if_exists is not None:
+        opts.append("if_exists=%r" % bool(op.if_exists))
     text = tmpl % {
         "prefix": _alembic_autogenerate_prefix(autogen_context),
         "name": _render_gen_name(autogen_context, op.index_name),
diff --git a/docs/build/unreleased/1446.rst b/docs/build/unreleased/1446.rst
new file mode 100644 (file)
index 0000000..fb54481
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: usecase, autogenerate
+
+    Render ``if_exists`` and ``if_not_exists`` parameters in
+    :class:`.CreateTableOp`, :class:`.CreateIndexOp`, :class:`.DropTableOp` and
+    :class:`.DropIndexOp` in an autogenerate context.  While Alembic does not
+    set these parameters during an autogenerate run, they can be enabled using
+    a custom :class:`.Rewriter` in the ``env.py`` file, where they will now be
+    part of the rendered Python code in revision files.  Pull request courtesy
+    of Louis-Amaury Chaib (@lachaib).
index 7907f7ec6262d003fc63a2fbf023c9463d4ac33a..14a33194fdead34fe29d82b1f02f35c33c9d4943 100644 (file)
@@ -93,6 +93,20 @@ class AutogenRenderTest(TestBase):
             "['active', 'code'], unique=False)",
         )
 
+    def test_render_add_index_if_not_exists(self):
+        """
+        autogenerate.render._add_index
+        """
+        t = self.table()
+        idx = Index("test_active_code_idx", t.c.active, t.c.code)
+        op_obj = ops.CreateIndexOp.from_index(idx)
+        op_obj.if_not_exists = True
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(self.autogen_context, op_obj),
+            "op.create_index('test_active_code_idx', 'test', "
+            "['active', 'code'], unique=False, if_not_exists=True)",
+        )
+
     @testing.emits_warning("Can't validate argument ")
     def test_render_add_index_custom_kwarg(self):
         t = self.table()
@@ -212,6 +226,20 @@ class AutogenRenderTest(TestBase):
             "op.drop_index('test_active_code_idx', table_name='test')",
         )
 
+    def test_drop_index_if_exists(self):
+        """
+        autogenerate.render._drop_index
+        """
+        t = self.table()
+        idx = Index("test_active_code_idx", t.c.active, t.c.code)
+        op_obj = ops.DropIndexOp.from_index(idx)
+        op_obj.if_exists = True
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(self.autogen_context, op_obj),
+            "op.drop_index('test_active_code_idx', table_name='test', "
+            "if_exists=True)",
+        )
+
     def test_drop_index_text(self):
         """
         autogenerate.render._drop_index
@@ -989,6 +1017,19 @@ class AutogenRenderTest(TestBase):
             "mysql_engine='InnoDB',sqlite_autoincrement=True)",
         )
 
+    def test_render_if_not_exists(self):
+        t = self.table()
+        op_obj = ops.CreateTableOp.from_table(t)
+        op_obj.if_not_exists = True
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(self.autogen_context, op_obj),
+            "op.create_table('test',"
+            "sa.Column('id', sa.Integer(), nullable=False),"
+            "sa.Column('active', sa.Boolean(), nullable=True),"
+            "sa.Column('code', sa.String(length=255), nullable=True),"
+            "sa.PrimaryKeyConstraint('id'),if_not_exists=True)",
+        )
+
     def test_render_drop_table(self):
         op_obj = ops.DropTableOp.from_table(Table("sometable", MetaData()))
         eq_ignore_whitespace(
@@ -1005,6 +1046,15 @@ class AutogenRenderTest(TestBase):
             "op.drop_table('sometable', schema='foo')",
         )
 
+    def test_render_drop_table_if_exists(self):
+        t = self.table()
+        op_obj = ops.DropTableOp.from_table(t)
+        op_obj.if_exists = True
+        eq_ignore_whitespace(
+            autogenerate.render_op_text(self.autogen_context, op_obj),
+            "op.drop_table('test', if_exists=True)",
+        )
+
     def test_render_table_no_implicit_check(self):
         m = MetaData()
         t = Table("test", m, Column("x", Boolean()))