SQLAlchemy:我应该如何使用对包含该列的表的引用来定义列的默认值?

时间:2014-07-16 22:34:27

标签: 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 (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中有类似的东西吗?

1 个答案:

答案 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,但这似乎是我们想要完成的事情的极端。