]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
return None for no parententity on Proxy
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Mar 2023 17:32:41 +0000 (13:32 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Mar 2023 17:33:09 +0000 (13:33 -0400)
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

doc/build/changelog/unreleased_20/9519.rst [new file with mode: 0644]
lib/sqlalchemy/orm/attributes.py
test/ext/test_hybrid.py

diff --git a/doc/build/changelog/unreleased_20/9519.rst b/doc/build/changelog/unreleased_20/9519.rst
new file mode 100644 (file)
index 0000000..c5a24a4
--- /dev/null
@@ -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.
index f33364c6247fa4cb2de712654b3d6dc25948565e..3a60eda4f298d4b59fb1adf62ab2dc7bb8f6817c 100644 (file)
@@ -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
 
index 73967cdd0072e9eabcb074d4b0e46d0bf0032266..69e9c133515c7d08e9a16f745a884c3469517a80 100644 (file)
@@ -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