]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
force uselist=False for all collection class not present
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Jan 2024 16:36:20 +0000 (11:36 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Jan 2024 17:01:32 +0000 (12:01 -0500)
Fixed issue where ORM Annotated Declarative would mis-interpret the left
hand side of a relationship without any collection specified as
uselist=True if the left type were given as a class and not a string,
without using future-style annotations.

Fixes: #10815
Change-Id: I85daccec03f7e6ea3b49eb07c06e0f85e361a1c0

doc/build/changelog/unreleased_20/10815.rst [new file with mode: 0644]
lib/sqlalchemy/orm/relationships.py
test/orm/declarative/test_tm_future_annotations_sync.py
test/orm/declarative/test_typed_mapping.py

diff --git a/doc/build/changelog/unreleased_20/10815.rst b/doc/build/changelog/unreleased_20/10815.rst
new file mode 100644 (file)
index 0000000..2240764
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 10815
+
+    Fixed issue where ORM Annotated Declarative would mis-interpret the left
+    hand side of a relationship without any collection specified as
+    uselist=True if the left type were given as a class and not a string,
+    without using future-style annotations.
index a82b7e24cb752a65190c6286bfdb0d516c9cc020..30cbec96a1ac29c73dcafb95c285163c0fade2de 100644 (file)
@@ -1814,15 +1814,12 @@ class RelationshipProperty(
                 argument, originating_module
             )
 
-            # we don't allow the collection class to be a
-            # __forward_arg__ right now, so if we see a forward arg here,
-            # we know there was no collection class either
-            if (
-                self.collection_class is None
-                and not is_write_only
-                and not is_dynamic
-            ):
-                self.uselist = False
+        if (
+            self.collection_class is None
+            and not is_write_only
+            and not is_dynamic
+        ):
+            self.uselist = False
 
         # ticket #8759
         # if a lead argument was given to relationship(), like
index b3b83b3de2cc95a6a74baab81abcea280e4bf761..d2f2a0261f367bbf3465c01b2b43ec3c5e5b18e7 100644 (file)
@@ -2750,7 +2750,7 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         is_false(B.__mapper__.attrs["a"].uselist)
         is_false(B.__mapper__.attrs["a_warg"].uselist)
 
-    def test_one_to_one_example(self, decl_base: Type[DeclarativeBase]):
+    def test_one_to_one_example_quoted(self, decl_base: Type[DeclarativeBase]):
         """test example in the relationship docs will derive uselist=False
         correctly"""
 
@@ -2774,6 +2774,32 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         is_(p1.child, c1)
         is_(c1.parent, p1)
 
+    def test_one_to_one_example_non_quoted(
+        self, decl_base: Type[DeclarativeBase]
+    ):
+        """test example in the relationship docs will derive uselist=False
+        correctly"""
+
+        class Child(decl_base):
+            __tablename__ = "child"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+            parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))
+            parent: Mapped["Parent"] = relationship(back_populates="child")
+
+        class Parent(decl_base):
+            __tablename__ = "parent"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+            child: Mapped[Child] = relationship(  # noqa: F821
+                back_populates="parent"
+            )
+
+        c1 = Child()
+        p1 = Parent(child=c1)
+        is_(p1.child, c1)
+        is_(c1.parent, p1)
+
     def test_collection_class_dict_no_collection(self, decl_base):
         class A(decl_base):
             __tablename__ = "a"
index 8dcf2013939dfe013046b51797b8ac2062e6e0af..37aa216d543577692644de941ea9e7253e657531 100644 (file)
@@ -2741,7 +2741,7 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         is_false(B.__mapper__.attrs["a"].uselist)
         is_false(B.__mapper__.attrs["a_warg"].uselist)
 
-    def test_one_to_one_example(self, decl_base: Type[DeclarativeBase]):
+    def test_one_to_one_example_quoted(self, decl_base: Type[DeclarativeBase]):
         """test example in the relationship docs will derive uselist=False
         correctly"""
 
@@ -2765,6 +2765,32 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         is_(p1.child, c1)
         is_(c1.parent, p1)
 
+    def test_one_to_one_example_non_quoted(
+        self, decl_base: Type[DeclarativeBase]
+    ):
+        """test example in the relationship docs will derive uselist=False
+        correctly"""
+
+        class Child(decl_base):
+            __tablename__ = "child"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+            parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))
+            parent: Mapped["Parent"] = relationship(back_populates="child")
+
+        class Parent(decl_base):
+            __tablename__ = "parent"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+            child: Mapped[Child] = relationship(  # noqa: F821
+                back_populates="parent"
+            )
+
+        c1 = Child()
+        p1 = Parent(child=c1)
+        is_(p1.child, c1)
+        is_(c1.parent, p1)
+
     def test_collection_class_dict_no_collection(self, decl_base):
         class A(decl_base):
             __tablename__ = "a"