Apply Custom Sorting to Table Columns within CREATE TABLE

This example illustrates use of the Rewriter object introduced at Fine-Grained Autogenerate Generation with Rewriters. While the rewriter grants access to the individual ops.MigrateOperation objects, there are sometimes some special techniques required to get around some structural limitations that are present.

此示例说明了 使用重写器在细粒度自动生成 中介绍的 Rewriter 对象的使用。 虽然重写器授予对单个 ops.MigrateOperation 对象的访问权限,但有时需要一些特殊技术来解决存在的一些结构限制。

One is when trying to reorganize the order of columns in a table within a ops.CreateTableOp directive. This directive, when generated by autogenerate, actually holds onto the original Table object as the source of its information, so attempting to reorder the ops.CreateTableOp.columns collection will usually have no effect. Instead, a new ops.CreateTableOp object may be constructed with the new ordering. However, a second issue is that the Column objects inside will already be associated with the Table that is from the model being autogenerated, meaning they can’t be reassigned directly to a new Table. To get around this, we can copy all the columns and constraints using methods like Column.copy().

一种是尝试在 ops.CreateTableOp 指令中重新组织表中列的顺序时。 该指令在由 autogenerate 生成时,实际上保留原始 Table 对象作为其信息的来源,因此尝试重新排序 ops.CreateTableOp.columns 集合通常不会有任何效果。 相反,可以使用新的排序构造一个新的 ops.CreateTableOp 对象。 然而,第二个问题是内部的 Column 对象已经与来自正在自动生成的模型的表相关联,这意味着它们不能直接重新分配给新表。 为了解决这个问题,我们可以使用 Column.copy() 等方法复制所有列和约束。

Below we use Rewriter to create a new ops.CreateTableOp directive and to copy the Column objects from one into another, copying each column or constraint object and applying a new sorting scheme:

下面我们使用 Rewriter 创建一个新的 ops.CreateTableOp 指令并将 Column 对象从一个复制到另一个,复制每个列或约束对象并应用新的排序方案:

# 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:

上面,当我们将 writer 应用于表时,例如:

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 ###