From: Mike Bayer Date: Tue, 3 Mar 2020 15:12:09 +0000 (-0500) Subject: Add custom table column sorting example X-Git-Tag: rel_1_4_2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=94ac796772b25ae3aafb83a846158fe8c3729c90;p=thirdparty%2Fsqlalchemy%2Falembic.git Add custom table column sorting example Change-Id: Ie953ba34ed8e6cbf90e3070494279174ebca5d1a --- diff --git a/docs/build/api/autogenerate.rst b/docs/build/api/autogenerate.rst index c4678e1f..230d42df 100644 --- a/docs/build/api/autogenerate.rst +++ b/docs/build/api/autogenerate.rst @@ -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: diff --git a/docs/build/cookbook.rst b/docs/build/cookbook.rst index 0d034af4..83cabdb8 100644 --- a/docs/build/cookbook.rst +++ b/docs/build/cookbook.rst @@ -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 ============================================