]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
support dataclasses with MutableComposite
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Jul 2022 18:30:36 +0000 (14:30 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Jul 2022 18:30:36 +0000 (14:30 -0400)
basically a one line change

Change-Id: If6a70d49777e77f86e2b6936dd4aece20b00708e

lib/sqlalchemy/ext/mutable.py
test/ext/test_mutable.py

index 06c46a9df0b2b64fd57ecf2197b1132ee8d2385a..6c26c9266f279ef80d6629bae89e49a98f7a56a4 100644 (file)
@@ -661,7 +661,8 @@ class MutableComposite(MutableBase):
 
             prop = parent.mapper.get_property(key)
             for value, attr_name in zip(
-                self.__composite_values__(), prop._attribute_keys
+                prop._composite_values_from_instance(self),
+                prop._attribute_keys,
             ):
                 setattr(parent.obj(), attr_name, value)
 
index 1ef9afbd87cf246806f6c69f0491467f79d92797..d130ab11f6682c8501212065f2a47fda316614f1 100644 (file)
@@ -1,4 +1,5 @@
 import copy
+import dataclasses
 import pickle
 
 from sqlalchemy import event
@@ -97,6 +98,44 @@ class MyPoint(Point):
         return value
 
 
+@dataclasses.dataclass
+class DCPoint(MutableComposite):
+    x: int
+    y: int
+
+    def __setattr__(self, key, value):
+        object.__setattr__(self, key, value)
+        self.changed()
+
+    def __getstate__(self):
+        return self.x, self.y
+
+    def __setstate__(self, state):
+        self.x, self.y = state
+
+
+@dataclasses.dataclass
+class MyDCPoint(MutableComposite):
+    x: int
+    y: int
+
+    def __setattr__(self, key, value):
+        object.__setattr__(self, key, value)
+        self.changed()
+
+    def __getstate__(self):
+        return self.x, self.y
+
+    def __setstate__(self, state):
+        self.x, self.y = state
+
+    @classmethod
+    def coerce(cls, key, value):
+        if isinstance(value, tuple):
+            value = MyDCPoint(*value)
+        return value
+
+
 class _MutableDictTestFixture:
     @classmethod
     def _type_fixture(cls):
@@ -1392,6 +1431,13 @@ class MutableCompositeColumnDefaultTest(
         assert f1 in sess.dirty
 
 
+class MutableDCCompositeColumnDefaultTest(MutableCompositeColumnDefaultTest):
+    @classmethod
+    def _type_fixture(cls):
+
+        return DCPoint
+
+
 class MutableCompositesUnpickleTest(_CompositeTestBase, fixtures.MappedTest):
     @classmethod
     def setup_mappers(cls):
@@ -1411,20 +1457,29 @@ class MutableCompositesUnpickleTest(_CompositeTestBase, fixtures.MappedTest):
             loads(dumps(u1))
 
 
+class MutableDCCompositesUnpickleTest(MutableCompositesUnpickleTest):
+    @classmethod
+    def _type_fixture(cls):
+
+        return DCPoint
+
+
 class MutableCompositesTest(_CompositeTestBase, fixtures.MappedTest):
     @classmethod
     def setup_mappers(cls):
         foo = cls.tables.foo
 
-        Point = cls._type_fixture()
+        cls.Point = cls._type_fixture()
 
         cls.mapper_registry.map_imperatively(
-            Foo, foo, properties={"data": composite(Point, foo.c.x, foo.c.y)}
+            Foo,
+            foo,
+            properties={"data": composite(cls.Point, foo.c.x, foo.c.y)},
         )
 
     def test_in_place_mutation(self):
         sess = fixture_session()
-        d = Point(3, 4)
+        d = self.Point(3, 4)
         f1 = Foo(data=d)
         sess.add(f1)
         sess.commit()
@@ -1432,11 +1487,11 @@ class MutableCompositesTest(_CompositeTestBase, fixtures.MappedTest):
         f1.data.y = 5
         sess.commit()
 
-        eq_(f1.data, Point(3, 5))
+        eq_(f1.data, self.Point(3, 5))
 
     def test_pickle_of_parent(self):
         sess = fixture_session()
-        d = Point(3, 4)
+        d = self.Point(3, 4)
         f1 = Foo(data=d)
         sess.add(f1)
         sess.commit()
@@ -1457,11 +1512,11 @@ class MutableCompositesTest(_CompositeTestBase, fixtures.MappedTest):
         f1 = Foo(data=None)
         sess.add(f1)
         sess.commit()
-        eq_(f1.data, Point(None, None))
+        eq_(f1.data, self.Point(None, None))
 
         f1.data.y = 5
         sess.commit()
-        eq_(f1.data, Point(None, 5))
+        eq_(f1.data, self.Point(None, 5))
 
     def test_set_illegal(self):
         f1 = Foo()
@@ -1476,7 +1531,7 @@ class MutableCompositesTest(_CompositeTestBase, fixtures.MappedTest):
 
     def test_unrelated_flush(self):
         sess = fixture_session()
-        f1 = Foo(data=Point(3, 4), unrelated_data="unrelated")
+        f1 = Foo(data=self.Point(3, 4), unrelated_data="unrelated")
         sess.add(f1)
         sess.flush()
         f1.unrelated_data = "unrelated 2"
@@ -1488,7 +1543,7 @@ class MutableCompositesTest(_CompositeTestBase, fixtures.MappedTest):
 
     def test_dont_reset_on_attr_refresh(self):
         sess = fixture_session()
-        f1 = Foo(data=Point(3, 4), unrelated_data="unrelated")
+        f1 = Foo(data=self.Point(3, 4), unrelated_data="unrelated")
         sess.add(f1)
         sess.flush()
 
@@ -1520,6 +1575,13 @@ class MutableCompositesTest(_CompositeTestBase, fixtures.MappedTest):
         eq_(f1.data.y, 15)
 
 
+class MutableDCCompositesTest(MutableCompositesTest):
+    @classmethod
+    def _type_fixture(cls):
+
+        return DCPoint
+
+
 class MutableCompositeCallableTest(_CompositeTestBase, fixtures.MappedTest):
     @classmethod
     def setup_mappers(cls):
@@ -1561,16 +1623,18 @@ class MutableCompositeCustomCoerceTest(
     def setup_mappers(cls):
         foo = cls.tables.foo
 
-        Point = cls._type_fixture()
+        cls.Point = cls._type_fixture()
 
         cls.mapper_registry.map_imperatively(
-            Foo, foo, properties={"data": composite(Point, foo.c.x, foo.c.y)}
+            Foo,
+            foo,
+            properties={"data": composite(cls.Point, foo.c.x, foo.c.y)},
         )
 
     def test_custom_coerce(self):
         f = Foo()
         f.data = (3, 4)
-        eq_(f.data, Point(3, 4))
+        eq_(f.data, self.Point(3, 4))
 
     def test_round_trip_ok(self):
         sess = fixture_session()
@@ -1580,7 +1644,14 @@ class MutableCompositeCustomCoerceTest(
         sess.add(f)
         sess.commit()
 
-        eq_(f.data, Point(3, 4))
+        eq_(f.data, self.Point(3, 4))
+
+
+class MutableDCCompositeCustomCoerceTest(MutableCompositeCustomCoerceTest):
+    @classmethod
+    def _type_fixture(cls):
+
+        return MyDCPoint
 
 
 class MutableInheritedCompositesTest(_CompositeTestBase, fixtures.MappedTest):
@@ -1606,16 +1677,18 @@ class MutableInheritedCompositesTest(_CompositeTestBase, fixtures.MappedTest):
         foo = cls.tables.foo
         subfoo = cls.tables.subfoo
 
-        Point = cls._type_fixture()
+        cls.Point = cls._type_fixture()
 
         cls.mapper_registry.map_imperatively(
-            Foo, foo, properties={"data": composite(Point, foo.c.x, foo.c.y)}
+            Foo,
+            foo,
+            properties={"data": composite(cls.Point, foo.c.x, foo.c.y)},
         )
         cls.mapper_registry.map_imperatively(SubFoo, subfoo, inherits=Foo)
 
     def test_in_place_mutation_subclass(self):
         sess = fixture_session()
-        d = Point(3, 4)
+        d = self.Point(3, 4)
         f1 = SubFoo(data=d)
         sess.add(f1)
         sess.commit()
@@ -1623,11 +1696,11 @@ class MutableInheritedCompositesTest(_CompositeTestBase, fixtures.MappedTest):
         f1.data.y = 5
         sess.commit()
 
-        eq_(f1.data, Point(3, 5))
+        eq_(f1.data, self.Point(3, 5))
 
     def test_pickle_of_parent_subclass(self):
         sess = fixture_session()
-        d = Point(3, 4)
+        d = self.Point(3, 4)
         f1 = SubFoo(data=d)
         sess.add(f1)
         sess.commit()
@@ -1642,3 +1715,10 @@ class MutableInheritedCompositesTest(_CompositeTestBase, fixtures.MappedTest):
             sess.add(f2)
             f2.data.y = 12
             assert f2 in sess.dirty
+
+
+class MutableInheritedDCCompositesTest(MutableInheritedCompositesTest):
+    @classmethod
+    def _type_fixture(cls):
+
+        return DCPoint