Skip to content

Commit 14d2bb0

Browse files
committed
- Fixed bug in ordering list where the order of items would be
thrown off during a collection replace event, if the reorder_on_append flag were set to True. The fix ensures that the ordering list only impacts the list that is explicitly associated with the object. fixes sqlalchemy#3191
1 parent 706d4fc commit 14d2bb0

File tree

5 files changed

+65
-2
lines changed

5 files changed

+65
-2
lines changed

doc/build/changelog/changelog_09.rst

+11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@
1313
.. changelog::
1414
:version: 0.9.8
1515

16+
.. change::
17+
:tags: bug, ext
18+
:verions: 1.0.0
19+
:tickets: 3191
20+
21+
Fixed bug in ordering list where the order of items would be
22+
thrown off during a collection replace event, if the
23+
reorder_on_append flag were set to True. The fix ensures that the
24+
ordering list only impacts the list that is explicitly associated
25+
with the object.
26+
1627
.. change::
1728
:tags: bug, sql
1829
:versions: 1.0.0

lib/sqlalchemy/ext/orderinglist.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class Bullet(Base):
119119
120120
121121
"""
122-
from ..orm.collections import collection
122+
from ..orm.collections import collection, collection_adapter
123123
from .. import util
124124

125125
__all__ = ['ordering_list']
@@ -319,7 +319,10 @@ def insert(self, index, entity):
319319

320320
def remove(self, entity):
321321
super(OrderingList, self).remove(entity)
322-
self._reorder()
322+
323+
adapter = collection_adapter(self)
324+
if adapter and adapter._referenced_by_owner:
325+
self._reorder()
323326

324327
def pop(self, index=-1):
325328
entity = super(OrderingList, self).pop(index)

lib/sqlalchemy/orm/collections.py

+10
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,16 @@ def data(self):
589589
"The entity collection being adapted."
590590
return self._data()
591591

592+
@property
593+
def _referenced_by_owner(self):
594+
"""return True if the owner state still refers to this collection.
595+
596+
This will return False within a bulk replace operation,
597+
where this collection is the one being replaced.
598+
599+
"""
600+
return self.owner_state.dict[self._key] is self._data()
601+
592602
@util.memoized_property
593603
def attr(self):
594604
return self.owner_state.manager[self._key].impl

test/ext/test_orderinglist.py

+22
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,28 @@ def test_replace(self):
349349
self.assert_(srt.bullets[1].text == 'new 2')
350350
self.assert_(srt.bullets[2].text == '3')
351351

352+
def test_replace_two(self):
353+
"""test #3191"""
354+
355+
self._setup(ordering_list('position', reorder_on_append=True))
356+
357+
s1 = Slide('Slide #1')
358+
359+
b1, b2, b3, b4 = Bullet('1'), Bullet('2'), Bullet('3'), Bullet('4')
360+
s1.bullets = [b1, b2, b3]
361+
362+
eq_(
363+
[b.position for b in s1.bullets],
364+
[0, 1, 2]
365+
)
366+
367+
s1.bullets = [b4, b2, b1]
368+
eq_(
369+
[b.position for b in s1.bullets],
370+
[0, 1, 2]
371+
)
372+
373+
352374
def test_funky_ordering(self):
353375
class Pos(object):
354376
def __init__(self):

test/orm/test_collection.py

+17
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,23 @@ class Foo(object):
21912191
f1.attr = l2
21922192
eq_(canary, [adapter_1, f1.attr._sa_adapter, None])
21932193

2194+
def test_referenced_by_owner(self):
2195+
2196+
class Foo(object):
2197+
pass
2198+
2199+
instrumentation.register_class(Foo)
2200+
attributes.register_attribute(
2201+
Foo, 'attr', uselist=True, useobject=True)
2202+
2203+
f1 = Foo()
2204+
f1.attr.append(3)
2205+
2206+
adapter = collections.collection_adapter(f1.attr)
2207+
assert adapter._referenced_by_owner
2208+
2209+
f1.attr = []
2210+
assert not adapter._referenced_by_owner
21942211

21952212

21962213

0 commit comments

Comments
 (0)