From: Mike Bayer Date: Tue, 21 Mar 2023 17:32:41 +0000 (-0400) Subject: return None for no parententity on Proxy X-Git-Tag: rel_2_0_8~20^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c8fb843a2d92e5d8d2271d46ddde02a2c79ee94c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git return None for no parententity on Proxy Fixed regression where accessing the expression value of a hybrid property on a class that was either unmapped or not-yet-mapped (such as calling upon it within a :func:`_orm.declared_attr` method) would raise an internal error, as an internal fetch for the parent class' mapper would fail and an instruction for this failure to be ignored were inadvertently removed in 2.0. Fixes: #9519 Change-Id: If195d26a5ddd2312a373004eb7a1403e1d11e7a4 --- diff --git a/doc/build/changelog/unreleased_20/9519.rst b/doc/build/changelog/unreleased_20/9519.rst new file mode 100644 index 0000000000..c5a24a4428 --- /dev/null +++ b/doc/build/changelog/unreleased_20/9519.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm + :tickets: 9519 + + Fixed regression where accessing the expression value of a hybrid property + on a class that was either unmapped or not-yet-mapped (such as calling upon + it within a :func:`_orm.declared_attr` method) would raise an internal + error, as an internal fetch for the parent class' mapper would fail and an + instruction for this failure to be ignored were inadvertently removed in + 2.0. diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index f33364c624..3a60eda4f2 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -611,11 +611,11 @@ def create_proxied_attribute( @property def _parententity(self): - return inspection.inspect(self.class_) + return inspection.inspect(self.class_, raiseerr=False) @property def parent(self): - return inspection.inspect(self.class_) + return inspection.inspect(self.class_, raiseerr=False) _is_internal_proxy = True diff --git a/test/ext/test_hybrid.py b/test/ext/test_hybrid.py index 73967cdd00..69e9c13351 100644 --- a/test/ext/test_hybrid.py +++ b/test/ext/test_hybrid.py @@ -15,7 +15,9 @@ from sqlalchemy import String from sqlalchemy import testing from sqlalchemy.ext import hybrid from sqlalchemy.orm import aliased +from sqlalchemy.orm import column_property from sqlalchemy.orm import declarative_base +from sqlalchemy.orm import declared_attr from sqlalchemy.orm import relationship from sqlalchemy.orm import Session from sqlalchemy.orm import synonym @@ -421,6 +423,61 @@ class PropertyExpressionTest(fixtures.TestBase, AssertsCompiledSQL): return A + def test_access_from_unmapped(self): + """test #9519""" + + class DnsRecord: + name = Column("name", String) + + @hybrid.hybrid_property + def ip_value(self): + return self.name[1:3] + + @ip_value.expression + def ip_value(cls): + return func.substring(cls.name, 1, 3) + + raw_attr = DnsRecord.ip_value + is_(raw_attr._parententity, None) + + self.assert_compile( + raw_attr, "substring(name, :substring_1, :substring_2)" + ) + + self.assert_compile( + select(DnsRecord.ip_value), + "SELECT substring(name, :substring_2, :substring_3) " + "AS substring_1", + ) + + def test_access_from_not_yet_mapped(self, decl_base): + """test #9519""" + + class DnsRecord(decl_base): + __tablename__ = "dnsrecord" + id = Column(Integer, primary_key=True) + name = Column(String, unique=False, nullable=False) + + @declared_attr + def thing(cls): + return column_property(cls.ip_value) + + name = Column("name", String) + + @hybrid.hybrid_property + def ip_value(self): + return self.name[1:3] + + @ip_value.expression + def ip_value(cls): + return func.substring(cls.name, 1, 3) + + self.assert_compile( + select(DnsRecord.thing), + "SELECT substring(dnsrecord.name, :substring_2, :substring_3) " + "AS substring_1 FROM dnsrecord", + ) + def test_labeling_for_unnamed(self, _unnamed_expr_fixture): A = _unnamed_expr_fixture