为现有数据库添加命名规范

16 投票
4 回答
7356 浏览
提问于 2025-04-17 20:31

我正在使用sqlalchemy,并尝试将alembic集成到数据库迁移中。

我的数据库已经存在,并且有一些外键(ForeignKey)没有命名。我想添加一个命名规则,以便在迁移时能够处理这些外键列。

我在我的models.py文件顶部添加了这里提供的命名规则: SQLAlchemy命名约束

convention = {
      "ix": 'ix_%(column_0_label)s',
      "uq": "uq_%(table_name)s_%(column_0_name)s",
      "ck": "ck_%(table_name)s_%(constraint_name)s",
      "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
      "pk": "pk_%(table_name)s"
              }

DeclarativeBase = declarative_base()


DeclarativeBase.metadata = MetaData(naming_convention=convention)

def db_connect():
   return create_engine(URL(**settings.DATABASE))

def create_reviews_table(engine):
    DeclarativeBase.metadata.create_all(engine)

class Review(DeclarativeBase):

    __tablename__ = 'reviews'

    id = Column(Integer, primary_key=True)
    review_id = Column('review_id', String, primary_key=True)
    resto_id = Column('resto_id', Integer, ForeignKey('restaurants.id'),
            nullable=True)
    url = Column('url', String),
    resto_name = Column('resto_name', String)

我按照教程的说明设置了alembic/env.py,把我的模型的元数据传递给target_metadata。

当我运行

$: alembic current

时,我遇到了以下错误: sqlalchemy.exc.InvalidRequestError: 命名约定包括%(constraint_name)s标记,要求约束必须明确命名。

在文档中,他们说“这个功能[使用命名约定生成列的名称]即使我们只使用Column.unique标志也会生效:” 1,所以我在想应该没有问题(他们还给出了一个没有命名的外键的例子)。

我需要回去给所有的约束明确命名,还是有办法自动处理这个问题?

4 个回答

1

这段话并不是要给出一个绝对的答案,也没有直接解决你的技术问题,但它可能涉及到一个“哲学问题”。也就是说,关于数据库的真相来源,你的SQLAlchemy代码可能是,也可能是关系数据库管理系统(RDMS)是。面对这种情况,如果两者都有部分信息,我认为可以考虑两种方法:

  1. 你正在探索的方法:你可以修改数据库的结构,让它和SQLAlchemy模型一致,这样你的Python代码就可以成为主控。这种方法最直观,但有时候可能因为技术或管理上的原因而无法实现。

  2. 接受RDMS中有SQLAlchemy没有的信息,但这些信息在日常工作中并不重要。你可以考虑使用其他的迁移工具(ETL),先对数据库进行“逆向工程”,然后再进行迁移。迁移完成后,你可以把新实例的控制权交还给SQLAlchemy(这可能需要对新的数据库或模型进行一些调整)。

哪种方法有效是无法确定的,因为每种方法都有自己的挑战。不过,我建议你可以考虑一下第二种方法。

6

这个错误信息告诉你,应该给约束(constraints)起个明确的名字。这里提到的约束是布尔值(Boolean)、枚举(Enum)等,但不是外键(foreign keys)或主键(primary keys)。

所以,你需要检查一下你的表格,凡是有布尔值或枚举的地方,都给它们加个名字。例如:

is_active = Column(Boolean(name='is_active'))

这就是你需要做的事情。

10

只需要把约定中的 "ck" 修改为 "ck": "ck_%(table_name)s_%(column_0_name)s"。这样就可以用了。

可以参考一下 sqlalchemy 的文档

撰写回答