我需要创建序列,但在通用情况下不使用Sequence
类。
USN = Column(Integer, nullable = False, default=nextusn, server_onupdate=nextusn)
,此函数nextusn
需要在模型中生成func.max(table.USN)
行的值。
我尝试使用此
class nextusn(expression.FunctionElement):
type = Numeric()
name = 'nextusn'
@compiles(nextusn)
def default_nextusn(element, compiler, **kw):
return select(func.max(element.table.c.USN)).first()[0] + 1
但是在此上下文元素中不知道element.table
。有解决方法吗?
答案 0 :(得分:0)
如果表为空,SELECT MAX()将返回NULL;您应该使用COALESCE来生成默认的“种子”值。见下文。
使用SELECT MAX插入行的整个方法对于并发使用来说完全不安全 - 所以你需要确保一次只有一个INSERT语句在表上调用,否则你可能会违反约束(你应该肯定在这个专栏上有某种约束。)
需要您的自定义元素才能知道实际的Column元素。我们可以通过在事实之后将“nextusn()”函数分配给Column来实现这一点,或者在下面我将使用事件显示更复杂的方法。
我不明白你的目的是什么“server_onupdate = nextusn”。 SQLAlchemy中的“server_onupdate”实际上并没有为您运行任何SQL,如果您创建了一个触发器,这是一个占位符;而且“SELECT MAX(id)FROM table”的东西是一个INSERT模式,我不确定你是否意味着在UPDATE发生任何事情。
@compiles扩展需要返回一个字符串,通过compiler.process()运行select()。见下文。
示例:
from sqlalchemy import Column, Integer, create_engine, select, func, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.expression import ColumnElement
from sqlalchemy.schema import ColumnDefault
from sqlalchemy.ext.compiler import compiles
from sqlalchemy import event
class nextusn_default(ColumnDefault):
"Container for a nextusn() element."
def __init__(self):
super(nextusn_default, self).__init__(None)
@event.listens_for(nextusn_default, "after_parent_attach")
def set_nextusn_parent(default_element, parent_column):
"""Listen for when nextusn_default() is associated with a Column,
assign a nextusn().
"""
assert isinstance(parent_column, Column)
default_element.arg = nextusn(parent_column)
class nextusn(ColumnElement):
"""Represent "SELECT MAX(col) + 1 FROM TABLE".
"""
def __init__(self, column):
self.column = column
@compiles(nextusn)
def compile_nextusn(element, compiler, **kw):
return compiler.process(
select([
func.coalesce(func.max(element.column), 0) + 1
]).as_scalar()
)
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, default=nextusn_default(), primary_key=True)
data = Column(String)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
# will normally pre-execute the default so that we know the PK value
# result.inserted_primary_key will be available
e.execute(A.__table__.insert(), data='single row')
# will run the default expression inline within the INSERT
e.execute(A.__table__.insert(), [{"data": "multirow1"}, {"data": "multirow2"}])
# will also run the default expression inline within the INSERT,
# result.inserted_primary_key will not be available
e.execute(A.__table__.insert(inline=True), data='single inline row')