Hi list,
my use case is along those lines, I want collections to maintain an
ordered list of items.
items can belong to many collections.
when an item is deleted, I'd like the list of items from the
collection to be updated, and ready to be changed again
the comments in the test from the code below show what's go wrong
I'm aware of the reorder_on_append option, but the source is
discouraging, I'm not even sure it is the way to go
reorder_on_append
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
variety of dangerous unexpected database writes.
so, my questions are :
- is there a way to obtain the expected behaviour ? how?
thanks
NIL
-
# -*- coding: utf-8 -*-
from nose.plugins.attrib import attr
import unittest
from sqlalchemy import (create_engine, Column, Integer,
String, ForeignKey, )
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.associationproxy import AssociationProxy
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
engine = create_engine('sqlite:///sqlaordering.db')
Session = sessionmaker(bind=engine)
session = Session()
from sqlalchemy.orm.interfaces import MapperExtension
class ReorderCollection(MapperExtension):
def after_delete(self, mapper, connection, instance):
# this isn't efficient at this stage
instance.collection._item_orders.reorder()
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name = Column(String(50))
_item_orders = relationship(ItemOrder,
cascade='all, delete-orphan',
backref='item',
)
def __init__(self, name):
self.name = name
def __repr__(self):
return '%r %r' \
% (self.__class__.__name__.capitalize(), self.name)
def _create_item_order(item):
return ItemOrder(item=item)
class Collection(Base):
__tablename__ = 'collections'
id = Column(Integer, primary_key=True)
name = Column(String(50))
_item_orders = relationship(ItemOrder,
cascade='all, delete-orphan',
backref='collection',
order_by=ItemOrder.position,
collection_class=ordering_list('position'),
)
items = AssociationProxy(_item_orders, item,
creator=_create_item_order)
def __init__(self, name):
self.name = name
def __repr__(self):
return '%r %r' \
% (self.__class__.__name__.capitalize(), self.name)
class ItemOrder(Base):
__tablename__ = 'item_orders'
id = Column(Integer, primary_key=True)
item_id = Column(Integer, ForeignKey('items.id'))
collection_id = Column(Integer, ForeignKey('collections.id'))
position = Column(Integer)
__mapper_args__ = {'extension': ReorderCollection()}
class TestOrderedRelation(unittest.TestCase):
def setUp(self):
Method used to build a database
Base.metadata.create_all(engine)
session.add_all([Item('T-Shirt'),
Item('Mug'),
Item('Hat'),
Item('Shoes'),
Collection('first'),
Collection('second'),
])
session.commit()
def tearDown(self):
Method used to destroy a database
Base.metadata.drop_all(engine)
@attr('do_it')
def test_problem(self):
first = session.query(Collection).first()
shirt = session.query(Item).get(1)
mug = session.query(Item).get(2)
hat = session.query(Item).get(3)
shoes = session.query(Item).get(4)
first.items.append(shirt)
first.items.append(mug)
first.items.append(mug)
first.items.append(shoes)
session.commit()
assert first.items == [shirt, mug, mug, shoes]
session.delete(mug)
session.commit()
# it seems nice
assert first.items == [shirt, shoes]
first.items.append(hat)
assert first.items == [shirt, shoes, hat]
# but at this point the positions are wrong
assert session.query(ItemOrder.position).all() == [(0,), (3,),
(2,)]
session.commit()
# the list 'magically' changed (no magic, just wrong index)
self.assertFalse(first.items == [shirt, shoes, hat])
# hat took index from len of list when it was inserted
assert first.items == [shirt, hat, shoes]
# worse indices are still wrong
assert