让SQLAlchemy在create_all上发出CREATE SCHEMA

Ror*_*art 22 python postgresql sqlalchemy

我有一个带有架构参数的SqlAlchemy模型,如下所示:

Base = declarative_base()

class Road(Base):
  __tablename__ = "roads"
  __table_args__ = {'schema': 'my_schema'}
  id = Column(Integer, primary_key=True)
Run Code Online (Sandbox Code Playgroud)

当我使用Base.metadata.create_all(engine)时,它正确地在前面发出一个带有模式名称的CREATE TABLE,就像这样"CREATE TABLE my_schema.roads("但Postgresql正确地抱怨模式不存在.

我错过了让SqlAlchemy发布CREATE SCHEMA my_schema的步骤,还是我必须手动调用它?

Ror*_*art 30

我已经在我的db init脚本上手动完成了它,如下所示:

from sqlalchemy.schema import CreateSchema
engine.execute(CreateSchema('my_schema'))
Run Code Online (Sandbox Code Playgroud)

但这似乎不像我期待的那么神奇.

  • 我必须先创建架构吗?独角兽在哪里? (25认同)

vic*_*vic 17

我遇到了同样的问题,并认为发布DDL的"最干净"方式是这样的:

from sqlalchemy import event
from sqlalchemy.schema import CreateSchema

event.listen(Base.metadata, 'before_create', CreateSchema('my_schema'))
Run Code Online (Sandbox Code Playgroud)

这将确保在创建基础元数据中包含的任何内容之前,您具有该模式.但是,这不会检查架构是否已存在.

CreateSchema('my_schema').execute_if(callback_=check_schema)如果你可以打扰编写check_schema回调(文档中的" 控制DDL序列 " should_create),你可以这样做.或者,作为一种简单的方法,只需使用DDL("CREATE SCHEMA IF NOT EXISTS my_schema")(对于Postgres):

from sqlalchemy import DDL

event.listen(Base.metadata, 'before_create', DDL("CREATE SCHEMA IF NOT EXISTS my_schema"))
Run Code Online (Sandbox Code Playgroud)


Mai*_*cio 5

metadata.tables您可以根据此 Github 问题评论进行迭代:

from sqlalchemy import event

@event.listens_for(Base.metadata, "before_create")
def create_schemas(target, connection, **kw):

    schemas = set()
    for table in target.tables.values():
        if table.schema is not None:
            schemas.add(table.schema)
    for schema in schemas:
        connection.execute("CREATE SCHEMA IF NOT EXISTS %s" % schema)


@event.listens_for(Base.metadata, "after_drop")
def drop_schemas(target, connection, **kw):

    schemas = set()
    for table in target.tables.values():
        if table.schema is not None:
            schemas.add(table.schema)
    for schema in schemas:
        connection.execute("DROP SCHEMA IF EXISTS %s" % schema)
Run Code Online (Sandbox Code Playgroud)

[编辑前]

我编写了一个函数,根据接受的答案创建声明的模式。它使用每个映射类的__table_args__字典中的模式值。

from sqlalchemy import event

@event.listens_for(Base.metadata, "before_create")
def create_schemas(target, connection, **kw):

    schemas = set()
    for table in target.tables.values():
        if table.schema is not None:
            schemas.add(table.schema)
    for schema in schemas:
        connection.execute("CREATE SCHEMA IF NOT EXISTS %s" % schema)


@event.listens_for(Base.metadata, "after_drop")
def drop_schemas(target, connection, **kw):

    schemas = set()
    for table in target.tables.values():
        if table.schema is not None:
            schemas.add(table.schema)
    for schema in schemas:
        connection.execute("DROP SCHEMA IF EXISTS %s" % schema)
Run Code Online (Sandbox Code Playgroud)

为什么他们不会默认创建模式,根据Michael Bayers对此Github 问题的评论:

[..]不幸的是,“模式”的概念在数据库之间的移植性不太好。在 MySQL 中,没有“CREATE SCHEMA”;只有其他数据库,例如用户帐户通常无权执行的“CREATE DATABASE”,此外还有许多与创建数据库相关的其他参数。在 SQLite 中,没有“CREATE SCHEMA”,您可以附加单独的文件。在 Oracle 中,没有“CREATE SCHEMA”,还有其他充当命名空间的用户帐户,它们同样具有非常不同的权限/语法,此外还有可以引用其他类型对象(例如远程表空间等)的同义词。只有 Postgresql 和 SQL Server 将“schema”链接到(通常)对应于“CREATE SCHEMA”的名称,即使如此,这些名称也可能是其他名称的符号名称,因为您可以将点符号和其他表达式放入“schema”中”。

使用元数据添加“CREATE SCHEMA”的简单方法只是一个简单的事件,并且可以将其添加到文档中 [...]