我使用原始 SQLAlchemy
engine.execute()
调用来执行 INSERT 语句,例如
engine = create_engine("mysql+pymysql://...")
engine.execute("INSERT INTO table VALUES (%s, %s, %s %s)", v1, v2, v3, v4)
现在假设第三列定义为
varchar(10) NOT NULL DEFAULT 'foo'
我想指定我想使用该默认值。
MySQL 为此有 DEFAULT 关键字,但我如何告诉 SQLAlchemy 为其中一个变量生成 DEFAULT 关键字?
我想象类似的事情
engine.execute("INSERT INTO table VALUES (%s, %s, %s %s)", v1, v2, default, v4)
但是我在这里需要
default
什么呢?
(SQLAlchemy 是否支持这一点?)
(我不能使用
None
,因为它会被转换为 NULL 并违反 NOT NULL 约束。(在 MySQL 8 上)
(我可以省略这样的列
engine.execute("INSERT INTO table (col1, col2, col4) VALUES (%s, %s, %s)", v1, v2, v4)
但我发现生成列名称列表是不可取的,我更喜欢使用 DEFAULT 关键字。)
这可以使用 SQLAlchemy 核心来实现,方法是传递名为“default”的列对象(或数据库用来表示默认值的任何内容),并将
is_literal
标志设置为 True
。这将导致“DEFAULT”被插入到不带引号的查询中。
import sqlalchemy as sa
engine = sa.create_engine('mysql+pymysql://root:@localhost/test', echo=True)
tbl = sa.Table(
'so75659729',
sa.MetaData(),
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('col_a', sa.String(32), server_default='foo', nullable=False),
sa.Column('col_b', sa.Integer, server_default='1', nullable=False),
sa.Column('col_c', sa.Integer, nullable=False),
)
tbl.drop(engine, checkfirst=True)
tbl.create(engine)
# Check that the dialect (probably the database) supports "DEFAULT" as a concept.
assert engine.dialect.supports_default_metavalue
# Get the value that the dialect uses for "DEFAULT".
default_token = engine.dialect.default_metavalue_token
# This is the parameter value.
default = sa.column(default_token, is_literal=True)
with engine.begin() as conn:
stmt = tbl.insert()
values = {
'id': default,
'col_a': default,
'col_b': default,
'col_c': 42,
}
conn.execute(stmt.values(**values))
这是参数绑定之前已编译的 SQL:
INSERT INTO so75659729 (id, col_a, col_b, col_c) VALUES (DEFAULT, DEFAULT, DEFAULT, %(col_c)s)
使用文本语句实现此目标更加困难,因为参数在绑定时会被引用。一种解决方案是这个答案中演示的技术 - 在绑定参数之前手动替换占位符。