]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
Add custom table column sorting example
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Mar 2020 15:12:09 +0000 (10:12 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Mar 2020 15:12:45 +0000 (10:12 -0500)
Change-Id: Ie953ba34ed8e6cbf90e3070494279174ebca5d1a

docs/build/api/autogenerate.rst
docs/build/cookbook.rst

index c4678e1fa2cd48d78c990a600b4b5310b036f8ce..230d42df71578407fa673245cf7c1b25a6b85d8d 100644 (file)
@@ -203,6 +203,16 @@ structure consisting of zero or more :class:`.MigrationScript` directives.
 The :func:`.command.revision` command will then produce scripts corresponding
 to whatever is in this list.
 
+.. seealso::
+
+    More examples of using :paramref:`.EnvironmentContext.configure.process_revision_directives`
+
+    :ref:`cookbook_no_empty_migrations`
+
+    :ref:`cookbook_dont_emit_drop_index`
+
+    :ref:`cookbook_custom_sorting_create_table`
+
 .. autofunction:: alembic.autogenerate.render_python_code
 
 .. _autogen_rewriter:
index 0d034af454aaed5128e322b6f7a32090b2abb557..83cabdb8c08129b139388edc3a4505376064a16d 100644 (file)
@@ -774,6 +774,8 @@ recreated again within the downgrade for this migration::
     INFO  [sqlalchemy.engine.base.Engine] {}
     INFO  [sqlalchemy.engine.base.Engine] COMMIT
 
+.. _cookbook_no_empty_migrations:
+
 Don't Generate Empty Migrations with Autogenerate
 =================================================
 
@@ -809,6 +811,8 @@ any operations::
             with context.begin_transaction():
                 context.run_migrations()
 
+.. _cookbook_dont_emit_drop_index:
+
 Don't emit DROP INDEX when the table is to be dropped as well
 =============================================================
 
@@ -934,6 +938,98 @@ the local :class:`.MetaData` collection::
         include_object = include_object
     )
 
+.. _cookbook_custom_sorting_create_table:
+
+Apply Custom Sorting to Table Columns within CREATE TABLE
+==========================================================
+
+This example illustrates use of the :class:`.Rewriter` object introduced
+at :ref:`autogen_rewriter`.   While the rewriter grants access to the
+individual :class:`.ops.MigrateOperation` objects, there are sometimes some
+special techniques required to get around some structural limitations that
+are present.
+
+One is when trying to reorganize the order of columns in a
+table within a :class:`.ops.CreateTableOp` directive.  This directive, when
+generated by autogenerate, actually holds onto the original :class:`.Table`
+object as the source of its information, so attempting to reorder the
+:attr:`.ops.CreateTableOp.columns` collection will usually have no effect.
+Instead, a new :class:`.ops.CreateTableOp` object may be constructed with the
+new ordering.   However, a second issue is that the :class:`.Column` objects
+inside will already be associated with the :class:`.Table` that is from the
+model being autogenerated, meaning they can't be reassigned directly to a new
+:class:`.Table`.  To get around this, we can copy all the columns and constraints
+using methods like :meth:`.Column.copy`.
+
+Below we use :class:`.Rewriter` to create a new :class:`.ops.CreateTableOp`
+directive and to copy the :class:`.Column` objects from one into another,
+copying each column or constraint object and applying a new sorting scheme::
+
+    # in env.py
+
+    from alembic.operations import ops
+    from alembic.autogenerate import rewriter
+
+    writer = rewriter.Rewriter()
+
+    @writer.rewrites(ops.CreateTableOp)
+    def order_columns(context, revision, op):
+
+        special_names = {"id": -100, "created_at": 1001, "updated_at": 1002}
+
+        cols_by_key = [
+            (
+                special_names.get(col.key, index)
+                if isinstance(col, Column)
+                else 2000,
+                col.copy(),
+            )
+            for index, col in enumerate(op.columns)
+        ]
+
+        columns = [
+            col for idx, col in sorted(cols_by_key, key=lambda entry: entry[0])
+        ]
+        return ops.CreateTableOp(
+            op.table_name, columns, schema=op.schema, **op.kw)
+
+
+    # ...
+
+    context.configure(
+        # ...
+        process_revision_directives=writer
+    )
+
+
+Above, when we apply the ``writer`` to a table such as::
+
+    Table(
+        "my_table",
+        m,
+        Column("data", String(50)),
+        Column("created_at", DateTime),
+        Column("id", Integer, primary_key=True),
+        Column("updated_at", DateTime),
+        UniqueConstraint("data", name="uq_data")
+    )
+
+
+This will render in the autogenerated file as::
+
+    def upgrade():
+        # ### commands auto generated by Alembic - please adjust! ###
+        op.create_table(
+            "my_table",
+            sa.Column("id", sa.Integer(), nullable=False),
+            sa.Column("data", sa.String(length=50), nullable=True),
+            sa.Column("created_at", sa.DateTime(), nullable=True),
+            sa.Column("updated_at", sa.DateTime(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+            sa.UniqueConstraint("data", name="uq_data"),
+        )
+        # ### end Alembic commands ###
+
 Don't emit CREATE TABLE statements for Views
 ============================================