A custom list that manages index/position information for contained elements.
orderinglist is a helper for mutable ordered relationships. It will
intercept list operations performed on a _orm.relationship
-managed
collection and
automatically synchronize changes in list position onto a target scalar
attribute.
Example: A slide table, where each row refers to zero or more entries in a related bullet table. The bullets within a slide are displayed in order based on the value of the position column in the bullet table. As entries are reordered in memory, the value of the position attribute should be updated to reflect the new sort order:
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)
The standard relationship mapping will produce a list-like attribute on each Slide containing all related Bullet objects, but coping with changes in ordering is not handled automatically. When appending a Bullet into Slide.bullets, the Bullet.position attribute will remain unset until manually assigned. When the Bullet is inserted into the middle of the list, the following Bullet objects will also need to be renumbered.
The .OrderingList
object automates this task, managing the
position attribute on all Bullet objects in the collection. It is
constructed using the .ordering_list
factory:
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)
With the above mapping the Bullet.position attribute is managed:
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
The .OrderingList
construct only works with changes to a
collection, and not the initial load from the database, and requires that the
list be sorted when loaded. Therefore, be sure to specify order_by on the
_orm.relationship
against the target ordering attribute, so that the
ordering is correct when first loaded.
Warning
.OrderingList
only provides limited functionality when a primary
key column or unique column is the target of the sort. Operations
that are unsupported or are problematic include:
- two entries must trade values. This is not supported directly in the case of a primary key or unique constraint because it means at least one row would need to be temporarily removed first, or changed to a third, neutral value while the switch occurs.
- an entry must be deleted in order to make room for a new entry. SQLAlchemy's unit of work performs all INSERTs before DELETEs within a single flush. In the case of a primary key, it will trade an INSERT/DELETE of the same primary key for an UPDATE statement in order to lessen the impact of this limitation, however this does not take place for a UNIQUE column. A future feature will allow the "DELETE before INSERT" behavior to be possible, alleviating this limitation, though this feature will require explicit configuration at the mapper level for sets of columns that are to be handled in this way.
.ordering_list
takes the name of the related object's ordering
attribute as an argument. By default, the zero-based integer index of the
object's position in the .ordering_list
is synchronized with the
ordering attribute: index 0 will get position 0, index 1 position 1, etc. To
start numbering at 1 or some other integer, provide count_from=1.
Author | |
Jason Kirtland |
Class | OrderingList |
A custom list that manages position information for its children. |
Function | count_from_0 |
Numbering function: consecutive integers starting at 0. |
Function | count_from_1 |
Numbering function: consecutive integers starting at 1. |
Function | count_from_n_factory |
Numbering function: consecutive integers starting at arbitrary start. |
Function | ordering_list |
Prepares an OrderingList factory for use in mapper definitions. |
Function | _reconstitute |
Reconstitute an .OrderingList . |
Function | _unsugar_count_from |
Builds counting functions from keyword arguments. |
Prepares an OrderingList
factory for use in mapper definitions.
Returns an object suitable for use as an argument to a Mapper relationship's collection_class option. e.g.:
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'))
Additional arguments are passed to the .OrderingList
constructor.
Parameters | |
attr | Name of the mapped attribute to use for storage and retrieval of ordering information |
count_from | Set up an integer-based ordering, starting at count_from. For example, ordering_list('pos', count_from=1) would create a 1-based list in SQL, storing the value in the 'pos' column. Ignored if ordering_func is supplied. |
**kw | Undocumented |