postgresql 让SQLAlchemy在create_all上发出CREATE SCHEMA

fhity93d  于 2023-04-29  发布在  PostgreSQL
关注(0)|答案(4)|浏览(202)

我有一个SqlAlchemy模型,其中的schema参数如下所示:

Base = declarative_base()

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

当我使用Base.metadata.create_all(engine)时,它正确地发出了一个CREATE TABLE,前面有模式名,比如CREATE TABLE my_schema.roads (,但是Postgresql正确地抱怨模式不存在。
我是否错过了让SqlAlchemy发出CREATE SCHEMA my_schema的步骤,或者我必须手动调用它?

w8ntj3qf

w8ntj3qf1#

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

from sqlalchemy.schema import CreateSchema
engine.execute(CreateSchema('my_schema'))

但这似乎没有我想象的那么神奇。

hfyxw5xn

hfyxw5xn2#

我也遇到了同样的问题,我相信发出DDL的“最干净”的方法是这样的:

from sqlalchemy import event
from sqlalchemy.schema import CreateSchema

event.listen(Base.metadata, 'before_create', CreateSchema('my_schema'))

这将确保在创建包含在基元数据中的任何内容之前,您已经拥有了它的模式。但是,这并不检查模式是否已经存在。
如果您愿意编写check_schema回调函数(文档中关于should_create的“控制DDL序列”),您可以执行CreateSchema('my_schema').execute_if(callback_=check_schema)。或者,作为一种简单的方法,使用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"))
nqwrtyyt

nqwrtyyt3#

你可以迭代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)

[编辑前]
我编写了一个函数,它根据接受的答案创建声明的模式。它使用每个Map类的**table_argsdict中的schema**值。

from sqlalchemy import event, DDL

# Import or write your mapped classes and configuration here

def init_db():
    for mapper in Base.registry.mappers:
        cls = mapper.class_
        if issubclass(cls, Base):
            table_args = getattr(cls, '__table_args__', None)
            if table_args:
                schema = table_args.get('schema')
                if schema:
                    stmt = f"CREATE SCHEMA IF NOT EXISTS {schema}"
                    event.listen(Base.metadata, 'before_create', DDL(stmt))
    Base.metadata.create_all(bind=engine)

为什么他们不会默认创建模式,根据Michael Bayers对this Github问题的评论:
[..]不幸的是,“模式”的概念在数据库之间的移植性不是很好。在MySQL中,没有“CREATE SCHEMA”;只有其他的数据库E。例如,“创建数据库”,用户帐户通常没有访问权限,另外还有很多其他参数与创建数据库沿着使用。在SQLite中,没有“CREATE SCHEMA”,有单独的文件可以附加。在Oracle中,没有“CREATE SCHEMA”,有其他用户帐户充当命名空间,它们同样具有非常不同的权限/语法,此外,还有可以引用其他类型的对象的同义词,如远程表空间等。只有PostgreSQL和SQL Server将“schema”链接到对应于(通常)“CREATE SCHEMA”的名称,即使这样,这些名称也可能是其他东西的符号名称,因为您可以将虚线符号和其他表达式放入“schema”中。
将“CREATE SCHEMA”与元数据一起抛出的简单方法只是一个简单的事件,并且可以将其添加到docs [。..]

uklbhaso

uklbhaso4#

这是the accepted answer(使用SQLAlchemy事件,看起来不错)和this more flexible answer的最佳组合,this more flexible answer不需要硬编码模式名称:

from sqlalchemy import Connection
from sqlalchemy import DDL
from sqlalchemy import event
from sqlalchemy import Table

@event.listens_for(Table, "before_create")
def create_schema_if_not_exists(target: Table, connection: Connection, **_):
    connection.execute(
        DDL("CREATE SCHEMA IF NOT EXISTS %(schema)s", {"schema": target.schema})
    )

这将创建一个由表指定的模式,如下所示:

class Application(Base):
    __tablename__ = "mytbl"
    __table_args__ = {"schema": "myschema"}

这很好用。

  • 注意:根据文档,你应该能够只做%(schema)s,它会神奇地工作,因为我们正在响应Table事件,但它没有。如果有人理解这部分文档的内容,我很乐意更新这个答案!*

相关问题