简体   繁体   English

SQLAlchemy。 创建共享枚举的表

[英]SQLAlchemy. Creating tables that share enum

Models FacebookPost and TwitterPost share an enum called types.模型 FacebookPost 和 TwitterPost 共享一个名为 types 的枚举。 This enum is correctly created when creating facebook_posts table, but when trying to create twitter_posts table, there is an attempt to recreate this type which results in an error.在创建 facebook_posts 表时正确创建了此枚举,但是在尝试创建 twitter_posts 表时,尝试重新创建此类型会导致错误。

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) type "types" already exists
 [SQL: "CREATE TYPE types AS ENUM ('Video', 'GIF', 'Scratch Reel', 'Card', 'Video Card', 'Text', 'Photo', 'Shared Article', 'Reply', 'Canvas', 'Carousel', 'Video Carousel', 'Link', 'Status')"]

This is the way I'm creating the database.这就是我创建数据库的方式。 I can't use Base.metadata.create_all, because I need to be explicit in terms of what tables are created我不能使用 Base.metadata.create_all,因为我需要明确创建哪些表

Engine = create_engine(db_url, echo=False)
Campaign.__table__.create(Engine)
SubCampaign.__table__.create(Engine)
Creative.__table__.create(Engine)
Hashtag.__table__.create(Engine)
FacebookPost.__table__.create(Engine)
TwitterPost.__table__.create(Engine)

I'm creating the enums this way:我正在以这种方式创建枚举:

from sqlalchemy import Enum
types = ('Video', 'GIF', 'Scratch Reel', 'Card', 'Video Card',
         'Text', 'Photo', 'Shared Article', 'Reply', 'Canvas',
         'Carousel', 'Video Carousel', 'Link', 'Status')
goals = ('CTR', 'ER', 'Awareness', 'CPGA')
sources = ('Facebook', 'Twitter', 'Instagram', 'Tumblr')

vars_ = locals().copy()
for k, v in vars_.items():
    if isinstance(v, tuple):
        locals()[k] = Enum(*v, name=k)

The generic Enum class does not offer any control over emitting the CREATE TYPE statement.泛型Enum类不提供对发出CREATE TYPE语句的任何控制。 But a PostgreSQL-specific alternative ENUM has a parameter create_type which can be used to disable it:但是特定于 PostgreSQL 的替代ENUM有一个参数create_type可用于禁用它:

from sqlalchemy.dialects.postgresql import ENUM


class TwitterPost(Base):
    ...
    type = Column("type", ENUM(*types, name="post_type", create_type=False))
    ...

I faced a similar problem in Alembic and used a workaround.我在 Alembic 中遇到了类似的问题并使用了一种解决方法。

The first example doesn't work.第一个例子不起作用。 SQLAlchemy creates the enum when create is called on it, but tries to create it again when it creates the tables, causing an error. SQLAlchemy 在调用createcreate枚举,但在创建表时尝试再次创建它,从而导致错误。

NEW_ENUM = sa.Enum(
    "A",
    "B",
    "C",
    name="my_enum",
    schema="my_schema"
)

NEW_ENUM.create(op.get_bind())

op.create_table(
    "table1",
    sa.MetaData(),
    sa.Column("id", sa.Integer, primary_key=True),
    sa.Column("column1", sa.String),
    sa.Column("column2", NEW_ENUM),
    schema="my_schema",
)

op.create_table(
    "table2",
    sa.MetaData(),
    sa.Column("id", sa.Integer, primary_key=True),
    sa.Column("column1", sa.Integer),
    sa.Column("column2", NEW_ENUM),
    schema="my_schema",
)

However, creating the tables without the enum columns and adding them afterwards works.但是,创建没有枚举列的表并随后添加它们是可行的。 The enum is created once on the database (Postgres in my case) and used for the two tables in the added columns:枚举在数据库上创建一次(在我的例子中是 Postgres)并用于添加列中的两个表:

NEW_ENUM = sa.Enum(
    "A",
    "B",
    "C",
    name="my_enum",
    schema="my_schema"
)

NEW_ENUM.create(op.get_bind())

op.create_table(
    "table1",
    sa.MetaData(),
    sa.Column("id", sa.Integer, primary_key=True),
    sa.Column("column1", sa.String),
    schema="my_schema",
)

op.add_column("table1", sa.Column("column2", NEW_ENUM), schema="my_schema")

op.create_table(
    "table2",
    sa.MetaData(),
    sa.Column("id", sa.Integer, primary_key=True),
    sa.Column("column1", sa.Integer),
    schema="my_schema",
)

op.add_column("table2", sa.Column("column2", NEW_ENUM), schema="my_schema")

For anyone using Alembic and having this issue.对于使用 Alembic 并遇到此问题的任何人。

There's also a create_type kwarg for postgresql.ENUM . postgresql.ENUM还有一个create_type kwarg。 which is used to set the schema for an enum type column in the alembic migration script.它用于在 alembic 迁移脚本中设置枚举类型列的架构。

Here's what my column definition looks like.这是我的列定义的样子。 (Which uses existing an existing enum) (使用现有的枚举)

sa.Column('ActionType', postgresql.ENUM('Primary', 'Secondary', name='actiontype', create_type=False), nullable=True),

This will now use the existing enum for the new column without creating a new one.这现在将使用现有的枚举作为新列而不创建新列。

I'm using SQLAlchemy==1.1.1 and alembic==0.8.8 in my requirements file.我在我的需求文件中使用SQLAlchemy==1.1.1alembic==0.8.8

I've given a similar answer here .我在这里给出了类似的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM