orderinglist — SQLAlchemy 0.5.6 Documentation

# SQLAlchemy 0.5.6 Documentation

Search:
Version: 0.5.6 Last Updated: 09/13/2009 10:45:11

# orderinglist

author: | Jason Kirtland

`orderinglist` is a helper for mutable ordered relations. It will intercept list operations performed on a relation collection and automatically synchronize changes in list position with an attribute on the related objects. (See Alternate Collection Implementations for more information on the general pattern.)

Example: Two tables that store slides in a presentation. Each slide has a number of bullet points, displayed in order by the ‘position’ column on the bullets table. These bullets can be inserted and re-ordered by your end users, and you need to update the ‘position’ column of all affected rows when changes are made.

    slides_table = Table('Slides', metadata,
                         Column('id', Integer, primary_key=True),
                         Column('name', String))

    bullets_table = Table('Bullets', metadata,
                          Column('id', Integer, primary_key=True),
                          Column('slide_id', Integer, ForeignKey('Slides.id')),
                          Column('position', Integer),
                          Column('text', String))

     class Slide(object):
         pass
     class Bullet(object):
         pass

     mapper(Slide, slides_table, properties={
           'bullets': relation(Bullet, order_by=[bullets_table.c.position])
     })
     mapper(Bullet, bullets_table)

The standard relation mapping will produce a list-like attribute on each Slide containing all related Bullets, but coping with changes in ordering is totally your responsibility. If you insert a Bullet into that list, there is no magic- it won’t have a position attribute unless you assign it it one, and you’ll need to manually renumber all the subsequent Bullets in the list to accommodate the insert.

An `orderinglist` can automate this and manage the ‘position’ attribute on all related bullets for you.

    mapper(Slide, slides_table, properties={
           'bullets': relation(Bullet,
                               collection_class=ordering_list('position'),
                               order_by=[bullets_table.c.position])
    })
    mapper(Bullet, bullets_table)

    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

Use the `ordering_list` function to set up the `collection_class` on relations (as in the mapper example above). This implementation depends on the list starting in the proper order, so be SURE to put an order_by on your relation.

`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`.

Ordering values are not limited to incrementing integers. Almost any scheme can implemented by supplying a custom `ordering_func` that maps a Python list index to any value you require. See the [module documentation](rel:docstrings_sqlalchemy.ext.orderinglist) for more information, and also check out the unit tests for examples of stepped numbering, alphabetical and Fibonacci numbering.

A custom list that manages index/position information for its children.

`orderinglist` is a custom list collection implementation for mapped relations that keeps an arbitrary “position” attribute on contained objects in sync with each object’s position in the Python list.

The collection acts just like a normal Python `list`, with the added behavior that as you manipulate the list (via `insert`, `pop`, assignment, deletion, what have you), each of the objects it contains is updated as needed to reflect its position. This is very useful for managing ordered relations which have a user-defined, serialized order:

    >>> from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey
    >>> from sqlalchemy.orm import mapper, relation
    >>> from sqlalchemy.ext.orderinglist import ordering_list

A simple model of users their “top 10” things:

    >>> metadata = MetaData()
    >>> users = Table('users', metadata,
    ...               Column('id', Integer, primary_key=True))
    >>> blurbs = Table('user_top_ten_list', metadata,
    ...               Column('id', Integer, primary_key=True),
    ...               Column('user_id', Integer, ForeignKey('users.id')),
    ...               Column('position', Integer),
    ...               Column('blurb', String(80)))
    >>> class User(object):
    ...   pass
    ...
    >>> class Blurb(object):
    ...    def __init__(self, blurb):
    ...        self.blurb = blurb
    ...
    >>> mapper(User, users, properties={
    ...  'topten': relation(Blurb, collection_class=ordering_list('position'),
    ...                     order_by=[blurbs.c.position])})
    <Mapper ...>
    >>> mapper(Blurb, blurbs)
    <Mapper ...>

Acts just like a regular list:

    >>> u = User()
    >>> u.topten.append(Blurb('Number one!'))
    >>> u.topten.append(Blurb('Number two!'))

But the `.position` attibute is set automatically behind the scenes:

    >>> assert [blurb.position for blurb in u.topten] == [0, 1]

The objects will be renumbered automaticaly after any list-changing operation, for example an `insert()`:

    >>> u.topten.insert(1, Blurb('I am the new Number Two.'))
    >>> assert [blurb.position for blurb in u.topten] == [0, 1, 2]
    >>> assert u.topten[1].blurb == 'I am the new Number Two.'
    >>> assert u.topten[1].position == 1

Numbering and serialization are both highly configurable. See the docstrings in this module and the main SQLAlchemy documentation for more information and examples.

The `ordering_list` factory function is the ORM-compatible constructor for OrderingList instances.

`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 relation’s `collection_class` option. Arguments are:

  attr
    Name of the mapped attribute to use for storage and retrieval of ordering information
  count_from (optional)
    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.

  Passes along any keyword arguments to `OrderingList` constructor.