diff options
-rw-r--r-- | CHANGES | 8 | ||||
-rwxr-xr-x | lib/sqlalchemy/ext/declarative.py | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/descriptor_props.py | 4 | ||||
-rw-r--r-- | test/ext/test_declarative.py | 63 |
4 files changed, 80 insertions, 5 deletions
@@ -35,6 +35,14 @@ CHANGES FROM clauses (like when you use union() or from_self()) when generating a cache key. +- declarative + - Fixed regression whereby composite() with + Column objects placed inline would fail + to initialize. The Column objects can now + be inline with the composite() or external + and pulled in via name or object ref. + [ticket:2058] + 0.7.0b1 ======= - Detailed descriptions of each change below are diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py index f80562b32..00c4aec3f 100755 --- a/lib/sqlalchemy/ext/declarative.py +++ b/lib/sqlalchemy/ext/declarative.py @@ -977,16 +977,17 @@ def _as_declarative(cls, classname, dict_): our_stuff.sort(key=lambda key: our_stuff[key]._creation_order) # extract columns from the class dict - cols = [] + cols = set() for key, c in our_stuff.iteritems(): if isinstance(c, (ColumnProperty, CompositeProperty)): for col in c.columns: - if isinstance(col, Column) and col.table is None: + if isinstance(col, Column) and \ + col.table is None: _undefer_column_name(key, col) - cols.append(col) + cols.add(col) elif isinstance(c, Column): _undefer_column_name(key, c) - cols.append(c) + cols.add(c) # if the column is the same name as the key, # remove it from the explicit properties dict. # the normal rules for assigning column-based properties @@ -994,6 +995,7 @@ def _as_declarative(cls, classname, dict_): # in multi-column ColumnProperties. if key == c.key: del our_stuff[key] + cols = sorted(cols, key=lambda c:c._creation_order) table = None if '__table__' not in dict_: diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 49f310787..d8a4bc8cf 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -164,6 +164,10 @@ class CompositeProperty(DescriptorProperty): prop = attr.property props.append(prop) + @property + def columns(self): + return [a for a in self.attrs if isinstance(a, schema.Column)] + def _setup_arguments_on_columns(self): """Propagate configuration arguments made on this composite to the target columns, for those that apply. diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index 9d75333c8..32eae69a9 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -10,7 +10,8 @@ from sqlalchemy import MetaData, Integer, String, ForeignKey, \ from test.lib.schema import Table, Column from sqlalchemy.orm import relationship, create_session, class_mapper, \ joinedload, configure_mappers, backref, clear_mappers, \ - polymorphic_union, deferred, column_property + polymorphic_union, deferred, column_property, composite,\ + Session from test.lib.testing import eq_ from sqlalchemy.util import classproperty from test.orm._base import ComparableEntity, MappedTest @@ -895,6 +896,66 @@ class DeclarativeTest(DeclarativeTestBase): self.assert_sql_count(testing.db, go, 1) + def test_composite_inline(self): + class AddressComposite(ComparableEntity): + def __init__(self, street, state): + self.street = street + self.state = state + def __composite_values__(self): + return [self.street, self.state] + + class User(Base, ComparableEntity): + __tablename__ = 'user' + id = Column(Integer, primary_key=True, + test_needs_autoincrement=True) + address = composite(AddressComposite, + Column('street', String(50)), + Column('state', String(2)), + ) + + Base.metadata.create_all() + sess = Session() + sess.add(User( + address=AddressComposite('123 anywhere street', + 'MD') + )) + sess.commit() + eq_( + sess.query(User).all(), + [User(address=AddressComposite('123 anywhere street', + 'MD'))] + ) + + def test_composite_separate(self): + class AddressComposite(ComparableEntity): + def __init__(self, street, state): + self.street = street + self.state = state + def __composite_values__(self): + return [self.street, self.state] + + class User(Base, ComparableEntity): + __tablename__ = 'user' + id = Column(Integer, primary_key=True, + test_needs_autoincrement=True) + street = Column(String(50)) + state = Column(String(2)) + address = composite(AddressComposite, + street, state) + + Base.metadata.create_all() + sess = Session() + sess.add(User( + address=AddressComposite('123 anywhere street', + 'MD') + )) + sess.commit() + eq_( + sess.query(User).all(), + [User(address=AddressComposite('123 anywhere street', + 'MD'))] + ) + def test_mapping_to_join(self): users = Table('users', Base.metadata, Column('id', Integer, primary_key=True) |