diff options
author | Jason Kirtland <jek@discorporate.us> | 2007-09-07 00:21:20 +0000 |
---|---|---|
committer | Jason Kirtland <jek@discorporate.us> | 2007-09-07 00:21:20 +0000 |
commit | 9206d7ed5a9e93f38de8b46cdb19c0c237fdb943 (patch) | |
tree | fd01503a2725d4b63691c0bb4f879d52f412fa6a /lib/sqlalchemy | |
parent | 22f48d6925e14a79cc92c70f6a2cd3f80f47268c (diff) | |
download | sqlalchemy-9206d7ed5a9e93f38de8b46cdb19c0c237fdb943.tar.gz |
Doc updates.
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/ext/orderinglist.py | 128 |
1 files changed, 97 insertions, 31 deletions
diff --git a/lib/sqlalchemy/ext/orderinglist.py b/lib/sqlalchemy/ext/orderinglist.py index e02990a26..e7464b0bd 100644 --- a/lib/sqlalchemy/ext/orderinglist.py +++ b/lib/sqlalchemy/ext/orderinglist.py @@ -1,27 +1,80 @@ +"""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.ext.orderinglist import ordering_list + + 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(Blurb, blurbs) + + u = User() + u.topten.append(Blurb('Number one!')) + u.topten.append(Blurb('Number two!')) + + # Like magic. + 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 [sqlalchemy.ext.orderinglist#ordering_list] function is the ORM-compatible +constructor for OrderingList instances. """ -A custom list implementation for mapped relations that syncs position in a -Python list with a position attribute on the mapped objects. -""" + __all__ = [ 'ordering_list' ] def ordering_list(attr, count_from=None, **kw): - """ - Prepares an OrderingList factory for use as an argument to a - Mapper relation's 'collection_class' option. Arguments are: + """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. + 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. + Passes along any keyword arguments to ``OrderingList`` constructor. """ kw = _unsugar_count_from(count_from=count_from, **kw) @@ -50,8 +103,11 @@ def count_from_n_factory(start): return f def _unsugar_count_from(**kw): - """Keyword argument filter, prepares a simple ordering_func from - a 'count_from' argument, otherwise passes ordering_func on unchanged.""" + """Builds counting functions from keywrod arguments. + + Keyword argument filter, prepares a simple ``ordering_func`` from a + ``count_from`` argument, otherwise passes ``ordering_func`` on unchanged. + """ count_from = kw.pop('count_from', None) if kw.get('ordering_func', None) is None and count_from is not None: @@ -64,36 +120,45 @@ def _unsugar_count_from(**kw): return kw class OrderingList(list): + """A custom list that manages position information for its children. + + See the module and __init__ documentation for more details. The + ``ordering_list`` function is used to configure ``OrderingList`` + collections in ``mapper`` relation definitions. + """ + def __init__(self, ordering_attr=None, ordering_func=None, reorder_on_append=False): - """ - A 'collection_class' list implementation that syncs position in a - Python list with a position attribute on the mapped objects. + """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 counts on the list starting in the proper - order, so be SURE to put an order_by on your relation. - Arguments are: + This implementation relies on the list starting in the proper order, + so be **sure** to put an ``order_by`` on your relation. ordering_attr Name of the attribute that stores the object's order in the relation. ordering_func Optional. A function that maps the position in the Python list to a - value to store in the ordering_attr. Values returned are usually + value to store in the ``ordering_attr``. Values returned are usually (but need not be!) integers. - ordering_funcs are called with two positional parameters: index of - the element in the list, and the list itself. + An ``ordering_func`` is called with two positional parameters: the + index of the element in the list, and the list itself. - If omitted, list indexes are used for the attribute values. Two - basic pre-built numbering functions are provided: 'count_from_0' and - 'count_from_1'. For more exotic examples like stepped numbering, - alphabetical and Fibonacci numbering, see the unit tests. + If omitted, Python list indexes are used for the attribute values. + Two basic pre-built numbering functions are provided in this module: + ``count_from_0`` and ``count_from_1``. For more exotic examples + like stepped numbering, alphabetical and Fibonacci numbering, see + the unit tests. reorder_on_append - Default false. When appending an object with an existing (non-None) + Default False. When appending an object with an existing (non-None) ordering value, that value will be left untouched unless - reorder_on_append is true. This is an optimization to avoid a + ``reorder_on_append`` is true. This is an optimization to avoid a variety of dangerous unexpected database writes. SQLAlchemy will add instances to the list via append() when your @@ -102,13 +167,14 @@ class OrderingList(list): '2', '3', and '4'), reorder_on_append=True would immediately renumber the items to '1', '2', '3'. If you have multiple sessions making changes, any of whom happen to load this collection even in - passing, all of the sessions would try to 'clean up' the numbering + passing, all of the sessions would try to "clean up" the numbering in their commits, possibly causing all but one to fail with a concurrent modification error. Spooky action at a distance. Recommend leaving this with the default of False, and just call - ._reorder() if you're doing append() operations with previously - ordered instances or doing housekeeping after manual sql operations. + ``_reorder()`` if you're doing ``append()`` operations with + previously ordered instances or when doing some housekeeping after + manual sql operations. """ self.ordering_attr = ordering_attr |