Python 在SQLAlchemy中标记表格中的一行

Python 在SQLAlchemy中标记表格中的一行,python,sqlalchemy,Python,Sqlalchemy,假设一个表有两列,id(整数)和mark(布尔值)。此表可以有任意数量的行,但其中只有一行的列mark应设置为True 如果我修改数据库以将另一个条目的标记标记为真,则系统应首先取消标记上一个条目,然后标记请求的条目 在Python/SQLAlchemy中如何处理这个问题?上面的两条评论都是正确的。触发器是一种很好的方法,而且“多假一真”模式表明,可能可以使用不同的表来引用“真”行,甚至整个“真”行可能位于其他位置。这里通常的模型是,您的表存储的是版本信息,“True”表示当前的“版本”。我通常

假设一个表有两列,
id
(整数)和
mark
(布尔值)。此表可以有任意数量的行,但其中只有一行的列
mark
应设置为
True

如果我修改数据库以将另一个条目的
标记
标记为
,则系统应首先取消标记上一个条目,然后标记请求的条目


在Python/SQLAlchemy中如何处理这个问题?

上面的两条评论都是正确的。触发器是一种很好的方法,而且“多假一真”模式表明,可能可以使用不同的表来引用“真”行,甚至整个“真”行可能位于其他位置。这里通常的模型是,您的表存储的是版本信息,“True”表示当前的“版本”。我通常从父记录引用“当前”版本,或者对所有“非当前”行使用一个名为“history”的单独表

不管怎样,让我们看看最快的方法来实现SQLAlchemy中的要求。我们将通过ORM事件完成插入/更新触发器的基本功能:

from sqlalchemy import Column, Integer, Boolean, create_engine
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import event
Base = declarative_base()

class Widget(Base):
    __tablename__ = 'widget'
    id = Column(Integer, primary_key=True)
    is_current_widget = Column(Boolean, default=False,
                        nullable=False)

@event.listens_for(Widget, "after_insert")
@event.listens_for(Widget, "after_update")
def _check_current(mapper, connection, target):
    if target.is_current_widget:
        connection.execute(
            Widget.__table__.
                update().
                values(is_current_widget=False).
                where(Widget.id!=target.id)
        )

e = create_engine('sqlite://', echo=True)
Base.metadata.create_all(e)

s = Session(e)

w1, w2, w3, w4, w5 = [Widget() for i in xrange(5)]
s.add_all([w1, w2, w3, w4, w5])
s.commit()

# note each call to commit() expires
# the values on all the Widgets so that
# is_current_widget is refreshed.

w2.is_current_widget = True
s.commit()

assert w2.is_current_widget
assert not w5.is_current_widget

w4.is_current_widget = True
s.commit()

assert not w2.is_current_widget
assert not w5.is_current_widget
assert w4.is_current_widget

# test the after_insert event

w6 = Widget(is_current_widget=True)
s.add(w6)
s.commit()

assert w6.is_current_widget
assert not w5.is_current_widget
assert not w4.is_current_widget

为什么不在数据库中使用触发器呢?这似乎更能防止错误。使用一个只用于标记一行的列不是很有效,特别是当您有很多行时。您可以考虑创建一个具有一行的单个表,该行存储“<代码> ID <代码>是‘标记’<代码> ID <代码>。这样,当条目被“标记”时,您只需更改一个值,而不必担心“取消标记”多个条目。强制执行约束的一种愚蠢(但大多数是可移植的)方法是使用一个可为null的CHAR(0)和一个唯一的约束。由于只有一个长度为零的字符串,因此只有一行可以具有非空值。