SQLAlchemy:我应该如何使用对包含该列的表的引用来定义列的默认值?
让我们以这些表为例(SQLite):
CREATE TABLE department (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE employee (
id INTEGER,
name TEXT NOT NULL,
department_id INTEGER NOT NULL,
FOREIGN KEY (department_id) REFERENCES department(id),
PRIMARY KEY (id, department_id)
);
我希望每个eployee的ID在其部门方面都是唯一的。在INSERT
上,应生成一个新的员工ID,该ID大于该部门中先前最高的员工ID。
放入原始SQL,这是我要做的事情:
INSERT INTO employee(
id,
name,
department_id
)
VALUES (
(
SELECT coalesce(MAX(id),0)+1
FROM employee
WHERE department_id=?
),
?,
?
)
使用SQLAlchemy执行此操作的最佳方法是什么?
我想我正在寻找类似于第三列示例的内容in here.这样的事情:
employee_table = Table("employee", meta,
Column('id', Integer, primary_key=True, autoincrement=False,
default=keyvalues.select(
func.max(employee_table.c.id)
).filter_by(department_id=??))
Column('department_id', Integer, ForeignKey('department.id'),
nullable=False, primary_key=True, autoincrement=False)
Column('name', String(127), nullable=False),
)
当然,这不起作用:我还没有引用employee
表(因为我还在定义它),因为我没有'我知道如何引用"当前" department_id
子句中的filter_by
。 (很可能还有其他问题)
或者,如果无法通过Python API执行此操作,我是否可以使用原始SQL指定列的默认值(在INSERT
时间应用)?或者我是否需要在整个插入中使用原始SQL?
注意:我的情况与this question中的情况基本相同,但我正在寻找的解决方案有所不同:我想在插入中使用嵌套SELECT
而不是创建一个数据库触发器。
修改
我越来越接近解决问题了,但我还没有解决问题。
agronholm
中的{p> #sqlalchemy
解释说,仅使用default
就无法填写department_id
,因为尽管可以选择INSERT
用作department_id
的默认值,无法填写参数(agronholm
)
相反,SELECT
建议最好的解决方案是在构造函数中创建子查询。通过分配查询(不运行它并分配结果!),id将在子SELECT
中获取。这避免了在Python端首先执行def __init__(self, department, name):
self.id = db.select(
db.func.max(Employee.id)
).filter_by(department_id=department.id).as_scalar()
self.department = department
self.data = data
然后分配结果所导致的竞争条件。
我正在尝试这样的事情:
InvalidRequestError: Instance <XXXXX at 0x3d15d10> cannot be refreshed - it's not persistent and does not contain a full primary key.
不幸的是,这也不起作用,因为计算的列用作主键的一部分。它抛出:
lastrowid
在我原来的raw-SQLite版本中,我将使用光标{{1}}访问新创建的行。 SQLAlchemy中有类似的东西吗?
答案 0 :(得分:0)
我遇到了类似的问题,终于找到了解决方案。还有改进的余地 - 它在INSERT之前执行SELECT而不是内联 - 但它似乎有效。
from sqlalchemy import sql
...
def default_employee_id(context):
return context.connection.execute(
sql.select(
[sql.func.ifnull(sql.func.max(employee_table.c.id), 0) + 1]
).where(
employee_table.c.department_id==context.current_parameters['department_id']
)
).scalar()
employee_table = Table("employee", meta,
Column('id', Integer, primary_key=True, autoincrement=False,
default=default_employee_id),
Column('department_id', Integer, ForeignKey('department.id'),
nullable=False, primary_key=True, autoincrement=False),
Column('name', String(127), nullable=False)
)
我要尝试的下一件事是trigger,即使文档说主键是个坏主意。 挂钩“before_flush”事件可能会有相同的预选问题。 也可以更改或替换context.compiled参数,以便将SELECT注入INSERT,但这似乎是我们想要完成的事情的极端。