【问题标题】:How to use SQLAlchemy models to generate raw SQL which is able to create all tables?如何使用 SQLAlchemy 模型生成能够创建所有表的原始 SQL?
【发布时间】:2021-12-05 04:45:44
【问题描述】:

可以生成原始 SQL 语句来创建任何 SQLAlchemy 表as shown here

但是,我想生成创建所有表的原始 SQL,而不仅仅是一个。

假设我有这样的表格(这个表格设计不是真实世界的,但它很好地说明了问题):

import sqlalchemy
from sqlalchemy import Column, Integer, Text, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import CreateTable
from sqlalchemy.dialects import postgresql

engine = sqlalchemy.create_engine('postgresql://postgres:postgres@127.0.0.1:5432/postgres', echo=True)
Base = declarative_base()


class Product(Base):
    __tablename__ = "product"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(Text, nullable=True)
    user_id = Column(Integer, ForeignKey('user.id'), nullable=True)

class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(Text, nullable=True)
    product_id = Column(Integer, ForeignKey('product.id'), nullable=True)


open("create_tables.sql", 'w').close() # to clear the file

for table in Base.metadata.tables.values():
    table_sql = str(CreateTable(table).compile(dialect=postgresql.dialect()))
    with open("create_tables.sql", "a") as myfile:
        myfile.write(table_sql)


Base.metadata.create_all(engine)

(这是完整的程序,您可以按原样运行)

在上面的代码中你可以看到我使用for循环来生成原始SQL:

for table in Base.metadata.tables.values():
    table_sql = str(CreateTable(table).compile(dialect=postgresql.dialect()))
    with open("create_tables.sql", "a") as myfile:
        myfile.write(table_sql)

生成的文件包含:

CREATE TABLE product (
    id SERIAL NOT NULL, 
    name TEXT, 
    user_id INTEGER, 
    PRIMARY KEY (id), 
    FOREIGN KEY(user_id) REFERENCES "user" (id)
)

CREATE TABLE "user" (
    id SERIAL NOT NULL, 
    name TEXT, 
    product_id INTEGER, 
    PRIMARY KEY (id), 
    FOREIGN KEY(product_id) REFERENCES product (id)
)

但是我不能执行这个 SQL 代码,由于循环引用它会给我错误。 (而且我至少需要添加一些;


SQLAlchemy 在我使用时可以毫无问题地创建这些表:

Base.metadata.create_all(engine)

echo=True 生成的 SQL 在日志中可见:

2021-10-18 05:37:24,442 INFO sqlalchemy.engine.Engine select version()
2021-10-18 05:37:24,443 INFO sqlalchemy.engine.Engine [raw sql] {}
2021-10-18 05:37:24,447 INFO sqlalchemy.engine.Engine select current_schema()
2021-10-18 05:37:24,447 INFO sqlalchemy.engine.Engine [raw sql] {}
2021-10-18 05:37:24,449 INFO sqlalchemy.engine.Engine show standard_conforming_strings
2021-10-18 05:37:24,450 INFO sqlalchemy.engine.Engine [raw sql] {}
2021-10-18 05:37:24,454 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2021-10-18 05:37:24,455 INFO sqlalchemy.engine.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%(name)s     
2021-10-18 05:37:24,456 INFO sqlalchemy.engine.Engine [generated in 0.00073s] {'name': 'product'}  
2021-10-18 05:37:24,459 INFO sqlalchemy.engine.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%(name)s     
2021-10-18 05:37:24,460 INFO sqlalchemy.engine.Engine [cached since 0.004542s ago] {'name': 'user'}2021-10-18 05:37:24,463 INFO sqlalchemy.engine.Engine
CREATE TABLE product (
        id SERIAL NOT NULL,
        name TEXT,
        user_id INTEGER,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
        PRIMARY KEY (id)
)


2021-10-18 05:37:24,464 INFO sqlalchemy.engine.Engine [no key 0.00116s] {}
2021-10-18 05:37:24,480 INFO sqlalchemy.engine.Engine
CREATE TABLE "user" (
        id SERIAL NOT NULL,
        name TEXT,
        product_id INTEGER,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
        PRIMARY KEY (id)
)


2021-10-18 05:37:24,481 INFO sqlalchemy.engine.Engine [no key 0.00128s] {}
2021-10-18 05:37:24,494 INFO sqlalchemy.engine.Engine ALTER TABLE product ADD FOREIGN KEY(user_id) 
REFERENCES "user" (id)
2021-10-18 05:37:24,495 INFO sqlalchemy.engine.Engine [no key 0.00053s] {}
2021-10-18 05:37:24,497 INFO sqlalchemy.engine.Engine ALTER TABLE "user" ADD FOREIGN KEY(product_id) REFERENCES product (id)
2021-10-18 05:37:24,497 INFO sqlalchemy.engine.Engine [no key 0.00047s] {}
2021-10-18 05:37:24,500 INFO sqlalchemy.engine.Engine COMMIT

我的问题是如何获取这条 SQL,它在上面的日志中可见,并且能够在一个事务中创建所有表,作为 Python 字符串(或.sql 文件)?

我希望能够对其进行检查/审查,然后手动将其(或部分)应用到生产服务器。

【问题讨论】:

    标签: python python-3.x postgresql sqlalchemy alembic


    【解决方案1】:

    你可以这样做:

    1. 将第一种类型的 DDL 生成到文件中;
    2. FOREIGN KEY(user_id) REFERENCES "user" (id) 等语句剪切/粘贴到文件末尾;
    3. 将它们编辑为ALTER TABLE product ADD FOREIGN KEY(user_id) REFERENCES "user" (id)(如第二个脚本);

    因此,您首先创建表,然后添加外键约束。这些是保持逻辑完整的安全且微不足道的更改。您的示例变为:

    CREATE TABLE product (
        id SERIAL NOT NULL, 
        name TEXT, 
        user_id INTEGER, 
        PRIMARY KEY (id) 
        -- cut FOREIGN KEY(user_id) REFERENCES "user" (id) and remove a comma
    );
    
    CREATE TABLE "user" (
        id SERIAL NOT NULL, 
        name TEXT, 
        product_id INTEGER, 
        PRIMARY KEY (id)
        -- Cut FOREIGN KEY(product_id) REFERENCES product (id) and remove a comma
    );
    
    -- Paste and edit here. Prepend "alter table <tablename> add "
    alter table product add FOREIGN KEY(user_id) REFERENCES "user" (id);
    alter table "user" add FOREIGN KEY(product_id) REFERENCES product (id);
    

    请注意语句后添加的分号。它们在生成的 DDL 中缺失。

    【讨论】:

      猜你喜欢
      • 2019-03-05
      • 2021-10-10
      • 2022-11-30
      • 1970-01-01
      • 2012-08-05
      • 1970-01-01
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多