Python SQLAlchemy:如何定义使用对包含该列的表的引用计算的列的默认值?
SQLAlchemy:如何定义使用对包含该列的表的引用计算的列的默认值 让我们将这些表用作SQLite示例:Python SQLAlchemy:如何定义使用对包含该列的表的引用计算的列的默认值?,python,sql,sqlalchemy,Python,Sql,Sqlalchemy,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 (departmen
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仅在其部门方面是唯一的。插入时,应生成一个新的员工ID,该ID比该部门以前最高的员工ID大一个
在原始SQL中,我希望执行以下操作:
INSERT INTO employee(
id,
name,
department_id
)
VALUES (
(
SELECT coalesce(MAX(id),0)+1
FROM employee
WHERE department_id=?
),
?,
?
)
使用SQLAlchemy实现这一点的最佳方法是什么
我想我正在寻找类似于第三列示例的东西,比如:
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),
)
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
当然,这是行不通的:我还没有对employee表的引用,因为我还在定义它,而且我不知道如何在filter\u by子句中引用当前的department\u id。很可能还有其他问题
或者,如果无法通过pythonapi实现这一点,是否可以使用原始SQL指定在插入时应用的列的默认值?还是需要对整个插入使用原始SQL
注意:我的情况与中基本相同,但我正在寻找的解决方案不同:我希望在插入中使用嵌套的SELECT,而不是创建DB触发器
编辑
我离解决这个问题越来越近了,但我还没有解决
sqlalchemy中的agronholm解释说,仅使用默认值无法填写部门id,因为尽管可以在插入时将可选项用作默认值,但无法填写部门id中的参数
相反,agronholm建议最好的解决方案是在构造函数中创建子查询。通过分配查询而不是运行查询并分配结果!,id将在子选择中获取。这避免了由于首先在Python端执行SELECT,然后分配结果而导致的竞争条件
我正在尝试这样的东西:
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),
)
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.
在原始SQLite版本中,我将使用光标的lastrowid访问新创建的行。SQLAlchemy中是否可能存在类似的问题?我遇到了类似的问题,最终找到了这个解决方案。仍然有改进的余地-它在插入之前进行选择,而不是内联-但似乎有效
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)
)
下一步我会尝试的是a,尽管文档说主键是个坏主意。
挂接到before_flush事件可能会有相同的预选问题。
也可以更改或替换context.compiled参数,以便将SELECT注入到INSERT中,但这对于我们试图实现的目标来说似乎是极端的。unique on ID比unique on ID强,DeptID,您是否有理由希望多个不同的员工具有相同的员工ID,或者只是你不在乎他们是否在乎?我也在寻找解决办法。对我来说,你的方法行不通。在我的例子中,所讨论的列是主键的一部分,我收到错误消息:InvalidRequestError:Instance无法刷新-它不是持久的,并且不包含完整的主键。@room2web不幸的是,我遇到了相同的问题。我刚刚更新了这个问题。如果您找到解决方案,请向我们报告@IfLoop我应该注意到我只是以员工/部门为例。这可能不是最好的选择,因为员工ID几乎肯定是全球唯一的。真正的用例有点复杂:子表存储大量不同类别的对象,在这些类别之间迁移对象是没有意义的。虽然每个类别的ID仍然不是严格需要的,但我们希望它们主要是出于美观的原因:这些ID在RESTAPI/中是公开可见的,并且我们希望它们增加单位increments@GabrielGrant在IRC频道询问后,我将问题提交给了谷歌集团。希望他们能帮助我们。