From e0befbb150321626aacfe79354e3215af562bc50 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 21 Dec 2010 15:58:28 -0500 Subject: [PATCH] - 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. --- CHANGES | 17 ++++--- lib/sqlalchemy/orm/properties.py | 2 +- test/orm/test_composites.py | 79 +++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index c360f1606f..eb14e43b23 100644 --- 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] diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index edfb861f4a..7ca57d3a4a 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -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): diff --git a/test/orm/test_composites.py b/test/orm/test_composites.py index 342cd819c4..8971e5b581 100644 --- a/test/orm/test_composites.py +++ b/test/orm/test_composites.py @@ -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')] + ) -- 2.47.3