2013-05-02 71 views
0

我需要創建序列,但在通用情況下不使用Sequence類。唯一序列號到列

USN = Column(Integer, nullable = False, default=nextusn, server_onupdate=nextusn) 

,這funcion 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

這是一個有點棘手,原因如下:

  1. 你的SELECT MAX()將返回NULL,如果表是空的;你應該使用COALESCE來產生默認的「種子」值。見下文。

  2. 用SELECT MAX插入行的整個方法對於併發使用是完全不安全的 - 因此您需要確保一次只能在表上調用一個INSERT語句,否則您可能會受到約束違規(您應該明確在這一列上有某種限制)。

  3. 從SQLAlchemy的角度來看,您需要自定義元素來了解實際的Column元素。我們可以通過在事件後將「nextusn()」函數分配給列來實現這一點,或者在下面我將展示使用事件的更復雜的方法。

  4. 我不明白「server_onupdate = nextusn」會帶來什麼。 SQLAlchemy中的「server_onupdate」實際上並沒有爲你運行任何SQL,如果你創建了一個觸發器,這是一個佔位符;而且「SELECT MAX(id)FROM table」是一個INSERT模式,我不確定你的意思是在UPDATE發生什麼。

  5. @compiles擴展需要返回一個字符串,通過compile.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') 
+0

答:1。是的。 2.是的,不安全。我試着用桌子。類似於序列。 3.是的。我是sqlalchemy的笨蛋。我現在明白了。我在更新這些行時遇到問題。我正在研究如何在表格中保留序列。謝謝你的回答。 – Jones 2013-05-06 03:48:18