diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-02-14 12:51:53 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-02-14 12:51:53 -0500 |
commit | 0cee3f6cf09d8f5c20e3f508685204bcf257e074 (patch) | |
tree | e21709c2f24ad507f5665dd48c20657c0585db25 | |
parent | 6458094024107e81eb7c9be140a1a385d26071a6 (diff) | |
download | sqlalchemy-0cee3f6cf09d8f5c20e3f508685204bcf257e074.tar.gz |
- 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]
-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) |