The proposed change will raise an UnmappedInstanceError instead of an attribute error if an instance an Instrumented Attribute is unmapped.
### Description
If a user tries to access an attribute of a database object that is unmapped then an error is raised. The reason for this is because the __get__ descriptor uses instance_state(instance) which uses the operator.attrgetter method to see if the object in question has the attribute "_sa_instance_state" that mapped objects have. If it doesn't contain this attribute it means that the object is unmapped.
This pull request is:
- [ ] A documentation / typographical error fix
- Good to go, no issue or tests are needed
- [X ] A short code fix
- please include the issue number, and create an issue if none exists, which
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests. one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
- please include the issue number, and create an issue if none exists, which must
include a complete example of how the feature would look.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.
**Have a nice day!**
Fixes: #3858
Closes: #5478
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5478
Pull-request-sha:
853051c9225446b69f52b13ade78709ad2617f6d
Change-Id: I5989c81227e55e628babdd11406d1e8ec0e8d93a
--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 3858
+
+ An ``UnmappedInstanceError`` is now raised for :class:`.InstrumentedAttribute`
+ if an instance is an unmapped object. Prior to this an ``AttributeError``
+ was raised. Pull request courtesy Ramon Williams.
if self._supports_population and self.key in dict_:
return dict_[self.key]
else:
- return self.impl.get(instance_state(instance), dict_)
+ try:
+ state = instance_state(instance)
+ except AttributeError as err:
+ util.raise_(
+ orm_exc.UnmappedInstanceError(instance),
+ replace_context=err,
+ )
+ return self.impl.get(state, dict_)
HasEntityNamespace = util.namedtuple(
lambda: Foo().bars.append(Bar()),
)
+ def test_unmapped_instance_raises(self):
+ class User(object):
+ pass
+
+ instrumentation.register_class(User)
+ attributes.register_attribute(
+ User, "user_name", uselist=False, useobject=False
+ )
+
+ class Blog(object):
+ name = User.user_name
+
+ def go():
+ b = Blog()
+ return b.name
+
+ assert_raises(
+ orm_exc.UnmappedInstanceError, go,
+ )
+
def test_del_scalar_nonobject(self):
class Foo(object):
pass