A custom list that manages index/position information for contained elements.
author: | Jason Kirtland |
---|
orderinglist
is a helper for mutable ordered relationships. It will
intercept list operations performed on a 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 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. Since changing the
order of entries often means that two rows must trade values, this is not
possible when the value is constrained by a primary key or unique
constraint, since one of the rows would temporarily have to point to a
third available value so that the other row could take its old
value. OrderingList
doesn’t do any of this for you,
nor does SQLAlchemy itself.
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
.
sqlalchemy.ext.orderinglist.
ordering_list
(attr, count_from=None, **kw)¶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'))
Parameters: |
|
---|
Additional arguments are passed to the OrderingList
constructor.
sqlalchemy.ext.orderinglist.
count_from_0
(index, collection)¶Numbering function: consecutive integers starting at 0.
sqlalchemy.ext.orderinglist.
count_from_1
(index, collection)¶Numbering function: consecutive integers starting at 1.
sqlalchemy.ext.orderinglist.
count_from_n_factory
(start)¶Numbering function: consecutive integers starting at arbitrary start.
sqlalchemy.ext.orderinglist.
OrderingList
(ordering_attr=None, ordering_func=None, reorder_on_append=False)¶Bases: __builtin__.list
A custom list that manages position information for its children.
The OrderingList
object is normally set up using the
ordering_list()
factory function, used in conjunction with
the relationship()
function.
__init__
(ordering_attr=None, ordering_func=None, reorder_on_append=False)¶A custom list that manages position information for its children.
OrderingList
is a collection_class
list implementation that
syncs position in a Python list with a position attribute on the
mapped objects.
This implementation relies on the list starting in the proper order,
so be sure to put an order_by
on your relationship.
Parameters: |
|
---|
append
(entity)¶L.append(object) – append object to end
insert
(index, entity)¶L.insert(index, object) – insert object before index
pop
([index]) → item -- remove and return item at index (default last).¶Raises IndexError if list is empty or index is out of range.
remove
(entity)¶L.remove(value) – remove first occurrence of value. Raises ValueError if the value is not present.
reorder
()¶Synchronize ordering for the entire collection.
Sweeps through the list and ensures that each object has accurate ordering information set.