排序列表

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

作者:

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。在主键的情况下,它将交换相同主键的 INSERT/DELETE 以进行 UPDATE 语句,以减少此限制的影响,但这不会发生在 UNIQUE 列上。未来的功能将允许“先 DELETE 后 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)

编号函数:从任意 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 工厂以用于映射器定义。

返回一个适合用作 Mapper 关系的 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 开始的连续整数。

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

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

function sqlalchemy.ext.orderinglist.count_from_n_factory(start)

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

class sqlalchemy.ext.orderinglist.OrderingList

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

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

类签名

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

method 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 为 true,否则该值将保持不变。这是一种优化,可以避免各种危险的意外数据库写入。

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

    建议将此项保留为默认值 False,并且如果您使用先前排序的实例执行 append() 操作,或者在手动 sql 操作后执行一些内务处理时,只需调用 reorder()

method sqlalchemy.ext.orderinglist.OrderingList.append(entity)

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

method sqlalchemy.ext.orderinglist.OrderingList.insert(index, entity)

在索引之前插入对象。

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

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

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

method sqlalchemy.ext.orderinglist.OrderingList.remove(entity)

删除值的首次出现。

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

method sqlalchemy.ext.orderinglist.OrderingList.reorder() None

同步整个集合的排序。

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