- Session weak_instance_dict=False is deprecated.
[ticket:1473]
+ - An exception is raised in the unusual case that an
+ append or similar event on a collection occurs after
+ the parent object has been dereferenced, which
+ prevents the parent from being marked as "dirty"
+ in the session. Was a warning in 0.6.6.
+ [ticket:2046]
+
- sql
- LIMIT/OFFSET clauses now use bind parameters
[ticket:805]
or exclude_properties would result in UnmappedColumnError.
[ticket:1995]
+ - A warning is emitted in the unusual case that an
+ append or similar event on a collection occurs after
+ the parent object has been dereferenced, which
+ prevents the parent from being marked as "dirty"
+ in the session. This will be an exception in 0.7.
+ [ticket:2046]
+
- sql
- Column.copy(), as used in table.tometadata(), copies the
'doc' attribute. [ticket:2028]
class UnmappedError(sa.exc.InvalidRequestError):
"""Base for exceptions that involve expected mappings not present."""
+class ObjectDereferencedError(sa.exc.SQLAlchemyError):
+ """An operation cannot complete due to an object being garbage collected."""
+
class DetachedInstanceError(sa.exc.SQLAlchemyError):
"""An attempt to access unloaded attributes on a
mapped instance that is detached."""
import weakref
from sqlalchemy import util
-from sqlalchemy.orm import exc as orm_exc, attributes, interfaces
+from sqlalchemy.orm import exc as orm_exc, attributes, interfaces,\
+ util as orm_util
from sqlalchemy.orm.attributes import PASSIVE_OFF, PASSIVE_NO_RESULT, \
PASSIVE_NO_FETCH, NEVER_SET, ATTR_WAS_SET, NO_VALUE
instance_dict._modified.add(self)
self._strong_obj = self.obj()
-
+ if self._strong_obj is None:
+ raise orm_exc.ObjectDereferencedError(
+ "Can't emit change event for attribute '%s' - parent object "
+ "of type %s has been garbage collected."
+ % (self.manager[attr.key], orm_util.state_class_str(self)))
self.modified = True
def commit(self, dict_, keys):
import pickle
-from sqlalchemy.orm import attributes, instrumentation
+from sqlalchemy.orm import attributes, instrumentation, exc as orm_exc
from sqlalchemy.orm.collections import collection
from sqlalchemy.orm.interfaces import AttributeExtension
from sqlalchemy import exc as sa_exc
from test.lib import *
-from test.lib.testing import eq_, ne_, assert_raises
+from test.lib.testing import eq_, ne_, assert_raises, assert_raises_message
from test.orm import _base
from test.lib.util import gc_collect, all_partial_orderings
from sqlalchemy.util import cmp, jython, topological
assert state.obj() is None
assert state.dict == {}
+ def test_object_dereferenced_error(self):
+ class Foo(object):
+ pass
+ class Bar(object):
+ def __init__(self):
+ gc_collect()
+
+ instrumentation.register_class(Foo)
+ instrumentation.register_class(Bar)
+ attributes.register_attribute(Foo,
+ 'bars',
+ uselist=True,
+ useobject=True)
+
+ assert_raises_message(
+ orm_exc.ObjectDereferencedError,
+ "Can't emit change event for attribute "
+ "'Foo.bars' - parent object of type <Foo> "
+ "has been garbage collected.",
+ lambda: Foo().bars.append(Bar())
+ )
+
def test_deferred(self):
class Foo(object):pass