]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug which prevented composite mapped
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Dec 2010 20:58:28 +0000 (15:58 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 21 Dec 2010 20:58:28 +0000 (15:58 -0500)
attributes from being used on a mapped select statement.
[ticket:1997]. Note the workings of composite are slated to
change significantly in 0.7.

CHANGES
lib/sqlalchemy/orm/properties.py
test/orm/test_composites.py

diff --git a/CHANGES b/CHANGES
index c360f1606f8d0ddc856950e8bdd85d226995f544..eb14e43b23cd0352c68de2f84925666a30a1c315 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -14,6 +14,17 @@ CHANGES
     that weren't previously saved in the "mutable changes"
     dictionary.
 
+  - Fixed bug which prevented composite mapped
+    attributes from being used on a mapped select statement. 
+    [ticket:1997]. Note the workings of composite are slated to 
+    change significantly in 0.7.
+
+  - active_history flag also added to composite(). 
+    The flag has no effect in 0.6, but is instead 
+    a placeholder flag for forwards compatibility,
+    as it applies in 0.7 for composites.
+    [ticket:1976]
+
   - Fixed uow bug whereby expired objects passed to 
     Session.delete() would not have unloaded references 
     or collections taken into account when deleting
@@ -44,12 +55,6 @@ CHANGES
     always load the "old" value, so that it's available to
     attributes.get_history(). [ticket:1961]
     
-  - active_history flag also added to composite(). 
-    The flag has no effect in 0.6, but is instead 
-    a placeholder flag for forwards compatibility,
-    as it will be needed in 0.7 for composites.
-    [ticket:1976]
-
   - Query.get() will raise if the number of params
     in a composite key is too large, as well as too 
     small. [ticket:1977]
index edfb861f4a87eeb1a18eb9a07ed1e75f3638f27e..7ca57d3a4ad6a71456c9b01889708a3f1c8bc5bd 100644 (file)
@@ -222,7 +222,7 @@ class CompositeProperty(ColumnProperty):
         if value is None:
             return None
         for a, b in zip(self.columns, value.__composite_values__()):
-            if a is column:
+            if a.shares_lineage(column): 
                 return b
 
     class Comparator(PropComparator):
index 342cd819c410cce521487ab737cdc74e46237ce7..8971e5b581451455a459cdee8d5101b916a05270 100644 (file)
@@ -1,7 +1,8 @@
 from sqlalchemy.test.testing import assert_raises, assert_raises_message
 import sqlalchemy as sa
 from sqlalchemy.test import testing
-from sqlalchemy import MetaData, Integer, String, ForeignKey, func, util
+from sqlalchemy import MetaData, Integer, String, ForeignKey, func, \
+    util, select
 from sqlalchemy.test.schema import Table, Column
 from sqlalchemy.orm import mapper, relationship, backref, \
     class_mapper,  \
@@ -376,3 +377,79 @@ class DefaultsTest(_base.MappedTest):
         
         assert f1.foob == FBComposite(2, 5, 15, None)
     
+class MappedSelectTest(_base.MappedTest):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table('descriptions', metadata,
+            Column('id', Integer, primary_key=True, 
+                            test_needs_autoincrement=True),
+            Column('d1', String(20)),
+            Column('d2', String(20)),
+        )
+
+        Table('values', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('description_id', Integer, ForeignKey('descriptions.id'),
+                   nullable=False),
+            Column('v1', String(20)),
+            Column('v2', String(20)),
+        )
+    
+    @classmethod
+    @testing.resolve_artifact_names
+    def setup_mappers(cls):
+        class Descriptions(_base.BasicEntity):
+            pass
+
+        class Values(_base.BasicEntity):
+            pass
+
+        class CustomValues(_base.BasicEntity, list):
+            def __init__(self, *args):
+                self.extend(args)
+
+            def __composite_values__(self):
+                return self
+
+        desc_values = select(
+            [values, descriptions.c.d1, descriptions.c.d2],
+            descriptions.c.id == values.c.description_id
+        ).alias('descriptions_values') 
+
+        mapper(Descriptions, descriptions, properties={
+            'values': relationship(Values, lazy='dynamic'),
+            'custom_descriptions': composite(
+                                CustomValues,
+                                        descriptions.c.d1,
+                                        descriptions.c.d2),
+
+        })
+
+        mapper(Values, desc_values, properties={
+            'custom_values': composite(CustomValues, 
+                                            desc_values.c.v1,
+                                            desc_values.c.v2),
+
+        })
+        
+    @testing.resolve_artifact_names
+    def test_set_composite_attrs_via_selectable(self):
+        session = Session()
+        d = Descriptions(
+            custom_descriptions = CustomValues('Color', 'Number'),
+            values =[
+                Values(custom_values = CustomValues('Red', '5')),
+                Values(custom_values=CustomValues('Blue', '1'))
+            ]
+        )
+
+        session.add(d)
+        session.commit()
+        eq_(
+            testing.db.execute(descriptions.select()).fetchall(),
+            [(1, u'Color', u'Number')]
+        )
+        eq_(
+            testing.db.execute(values.select()).fetchall(),
+            [(1, 1, u'Red', u'5'), (2, 1, u'Blue', u'1')]
+        )