排序列表

一个自定义列表,用于管理包含元素的索引/位置信息。

作者:

Jason Kirtland

orderinglist 是用于可变排序关系的助手。它将拦截对 relationship() 管理的集合执行的列表操作,并自动将列表位置的变化同步到目标标量属性。

示例:一个 slide 表,其中每一行都引用相关 bullet 表中的零个或多个条目。幻灯片中的项目根据 bullet 表中 position 列的值按顺序显示。当在内存中对条目重新排序时,position 属性的值应更新以反映新的排序顺序。

Base = declarative_base()

class Slide(Base):
    __tablename__ = 'slide'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    bullets = relationship("Bullet", order_by="Bullet.position")

class Bullet(Base):
    __tablename__ = 'bullet'
    id = Column(Integer, primary_key=True)
    slide_id = Column(Integer, ForeignKey('slide.id'))
    position = Column(Integer)
    text = Column(String)

标准关系映射将在每个 Slide 上生成一个类似列表的属性,其中包含所有相关的 Bullet 对象,但不会自动处理排序的变化。当将 Bullet 追加到 Slide.bullets 时,Bullet.position 属性将保持未设置状态,直到手动分配。当将 Bullet 插入列表的中间时,后续的 Bullet 对象也需要重新编号。

OrderingList 对象自动执行此任务,管理集合中所有 Bullet 对象上的 position 属性。它是使用 ordering_list() 工厂构建的。

from sqlalchemy.ext.orderinglist import ordering_list

Base = declarative_base()

class Slide(Base):
    __tablename__ = 'slide'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    bullets = relationship("Bullet", order_by="Bullet.position",
                            collection_class=ordering_list('position'))

class Bullet(Base):
    __tablename__ = 'bullet'
    id = Column(Integer, primary_key=True)
    slide_id = Column(Integer, ForeignKey('slide.id'))
    position = Column(Integer)
    text = Column(String)

使用上面的映射,将管理 Bullet.position 属性。

s = Slide()
s.bullets.append(Bullet())
s.bullets.append(Bullet())
s.bullets[1].position
>>> 1
s.bullets.insert(1, Bullet())
s.bullets[2].position
>>> 2

OrderingList 结构仅适用于对集合的更改,而不适用于从数据库的初始加载,并且要求在加载时列表已排序。因此,请确保在 relationship() 上针对目标排序属性指定 order_by,以便在首次加载时排序正确。

警告

当主键列或唯一列是排序的目标时,OrderingList 仅提供有限的功能。不支持或存在问题的操作包括

  • 两个条目必须交换值。在主键或唯一约束的情况下,这直接不支持,因为这意味着至少需要先暂时删除一行,或更改为第三个中性值,然后再进行交换。

  • 必须删除一个条目才能为新条目腾出空间。SQLAlchemy 的工作单元在单个刷新中执行所有 INSERT 之前执行所有 DELETE。在主键的情况下,它将用 UPDATE 语句来交换具有相同主键的 INSERT/DELETE,以减轻此限制的影响,但是对于 UNIQUE 列,这不会发生。未来的功能将允许“DELETE before INSERT”行为,这将缓解此限制,尽管此功能将需要在映射器级别针对要以这种方式处理的列集进行显式配置。

ordering_list() 接受相关对象排序属性的名称作为参数。默认情况下,对象在 ordering_list() 中位置的基于零的整数索引将与排序属性同步:索引 0 将获得位置 0,索引 1 将获得位置 1,依此类推。要从 1 或其他整数开始编号,请提供 count_from=1

API 参考

对象名称 描述

count_from_0(index, collection)

编号函数:从 0 开始的连续整数。

count_from_1(index, collection)

编号函数:从 1 开始的连续整数。

count_from_n_factory(start)

编号函数:从任意开始值开始的连续整数。

ordering_list(attr[, count_from, ordering_func, reorder_on_append])

准备一个 OrderingList 工厂,以便在映射器定义中使用。例如:

OrderingList

一个自定义列表,用于管理其子项的位置信息。

function sqlalchemy.ext.orderinglist.ordering_list(attr: str, count_from: int | None = None, ordering_func: Callable[[int, Sequence[_T]], int] | None = None, reorder_on_append: bool = False) Callable[[], OrderingList]

准备一个 OrderingList 工厂,以便在映射器定义中使用。例如:

返回一个适合用作映射器关系 collection_class 选项参数的对象。例如:

from sqlalchemy.ext.orderinglist import ordering_list

class Slide(Base):
    __tablename__ = 'slide'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    bullets = relationship("Bullet", order_by="Bullet.position",
                            collection_class=ordering_list('position'))
参数:
  • attr – 用于存储和检索排序信息的映射属性的名称

  • count_from – 设置基于整数的排序,从 count_from 开始。例如,ordering_list('pos', count_from=1) 将在 SQL 中创建一个基于 1 的列表,并将值存储在 ‘pos’ 列中。如果提供了 ordering_func,则忽略此参数。

其他参数将传递给 OrderingList 构造函数。

function sqlalchemy.ext.orderinglist.count_from_0(index, collection)

编号函数:从 0 开始的连续整数。

函数 sqlalchemy.ext.orderinglist.count_from_1(index, collection)

编号函数:从 1 开始的连续整数。

函数 sqlalchemy.ext.orderinglist.count_from_n_factory(start)

编号函数:从任意开始值开始的连续整数。

sqlalchemy.ext.orderinglist.OrderingList

一个自定义列表,用于管理其子项的位置信息。

OrderingList 对象通常使用 ordering_list() 工厂函数设置,与 relationship() 函数结合使用。

类签名

class sqlalchemy.ext.orderinglist.OrderingList (builtins.list, typing.Generic)

方法 sqlalchemy.ext.orderinglist.OrderingList.__init__(ordering_attr: str | None = None, ordering_func: Callable[[int, Sequence[_T]], int] | None = None, reorder_on_append: bool = False)

一个自定义列表,用于管理其子项的位置信息。

OrderingList 是一个 collection_class 列表实现,它将 Python 列表中的位置与映射对象上的位置属性同步。

此实现依赖于列表以正确的顺序开始,所以务必在您的关系上添加一个 order_by

参数:
  • ordering_attr – 存储对象在关系中的顺序的属性名称。

  • ordering_func

    可选。一个函数,将 Python 列表中的位置映射到存储在 ordering_attr 中的值。返回值通常(但不必)是整数。

    一个 ordering_func 用两个位置参数调用:列表中元素的索引和列表本身。

    如果省略,Python 列表索引将用于属性值。本模块中提供了两个基本的预制编号函数:count_from_0count_from_1。有关更多奇特的例子,例如步进编号、字母顺序和斐波那契编号,请参见单元测试。

  • reorder_on_append

    默认值为 False。当追加一个具有现有(非 None)排序值的物体时,除非 reorder_on_append 为真,否则该值将保持不变。这是为了避免各种危险的意外数据库写入而进行的优化。

    当您的对象加载时,SQLAlchemy 会通过 append() 将实例添加到列表中。如果出于某种原因,来自数据库的结果集在排序中跳过了一步(例如,缺少行“1”但您获得了“2”、“3”和“4”),则 reorder_on_append=True 会立即将项目重新编号为“1”、“2”、“3”。如果您有多个会话进行更改,其中任何一个碰巧加载了此集合,即使只是传递,所有会话都会尝试在其提交中“清理”编号,可能导致除一个以外的所有会话都出现并发修改错误。

    建议保持默认值 False,并在进行 append() 操作时使用以前排序的实例,或者在执行一些手动 sql 操作后的整理时,仅调用 reorder()

方法 sqlalchemy.ext.orderinglist.OrderingList.append(entity)

将对象追加到列表的末尾。

方法 sqlalchemy.ext.orderinglist.OrderingList.insert(index, entity)

在索引之前插入对象。

方法 sqlalchemy.ext.orderinglist.OrderingList.pop(index=-1)

删除并返回索引处的项目(默认值为最后一个)。

如果列表为空或索引超出范围,则引发 IndexError。

方法 sqlalchemy.ext.orderinglist.OrderingList.remove(entity)

删除值的第一个出现。

如果值不存在,则引发 ValueError。

方法 sqlalchemy.ext.orderinglist.OrderingList.reorder() None

同步整个集合的排序。

扫描列表并确保每个对象都设置了准确的排序信息。