1
想象一下具有两列的表格id
(Integer)和mark
(布尔值)。该表格可以有任意数量的行,但其中的一行应该将mark
列设置为True
。在SQLAlchemy的表中标记单个表格
如果我修改数据库以标记另一个条目的mark
到True
,那么系统应该先取消标记先前的条目,然后标记所请求的条目。
你将如何在Python/SQLAlchemy中处理这个问题?
想象一下具有两列的表格id
(Integer)和mark
(布尔值)。该表格可以有任意数量的行,但其中的一行应该将mark
列设置为True
。在SQLAlchemy的表中标记单个表格
如果我修改数据库以标记另一个条目的mark
到True
,那么系统应该先取消标记先前的条目,然后标记所请求的条目。
你将如何在Python/SQLAlchemy中处理这个问题?
上面的两条评论对他们都是有道理的。一个触发器是一个很好的方法来做到这一点,而且“许多错误的,一个真实的”模式表明,也许可以使用不同的表来引用“真实”行,甚至可以将整个“真实”行引用到别处。这里通常的模型是你的表存储版本信息,“真”代表当前的“版本”。我通常要么从父记录引用“当前”版本,要么对所有“非当前”行使用一个名为“history”的单独表。
无论如何,让我们看看在SQLAlchemy中完成所要求的最快捷方式。我们将通过ORM事件来做一些INSERT/UPDATE触发器:
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
为什么不在数据库中使用触发器来做呢?这似乎更加防错。 – 2011-06-07 18:03:01
有一个只能标记一行的列是不是非常有效,特别是如果你有很多很多的行。你可能会考虑用单行来创建一个单独的表,它存储哪个'id'是'标记''id'。通过这种方式,当条目被“标记”时,您只需要更改一个值,而不必担心“未标记”多个条目。 – Raceyman 2011-06-07 20:20:55
一个愚蠢的(但大多是便携式的)强制执行约束的方法是使用一个具有唯一约束的可为空的CHAR(0)。由于只有一个长度为零的字符串,因此只有一行可能具有非空值。 – SingleNegationElimination 2011-06-13 01:59:59