declarative syntax does not work with column_mapped_collection

Issue #1174 resolved
Former user created an issue

Using SA 0.5.0rc1.

I am using a pattern which boils down to this:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import orm
from sqlalchemy import schema
from sqlalchemy import types
from sqlalchemy.orm import collections

BaseObject = declarative_base()

class Foo(BaseObject):
    __tablename__ = "foo"
    id = schema.Column(types.Integer(), primary_key=True, autoincrement=True)

class Bar(BaseObject):
    __tablename__ = "bar"
    id = schema.Column(types.Integer(), primary_key=True, autoincrement=True)
    foos = orm.relation(Foo, lazy=False,
       collection_class=collections.column_mapped_collection(Foo.id))

this results in an error when initializing my models:

  File "/local/buildout/eggs/tmpEDqA0a/SQLAlchemy-0.5.0rc1-py2.5.egg/sqlalchemy/orm/collections.py", line 140, in column_mapped_collection
TypeError: '_CompileOnAttr' object is not iterable

Comments (4)

  1. Former user Account Deleted

    Relevant irc comments:

    16:26 <jek> Foo.id is not a Column
    16:27 <jek> Foo.__table__.c.id is what you're looking for there
    16:27 <zzzeek_> jek: we should put converters there
    16:28 <zzzeek_> i.e. to call __clause_element__()
    

    as an alternative you can also use attribute_mapped_column("id") here.

  2. Mike Bayer repo owner

    heres a patch:

    Index: lib/sqlalchemy/orm/properties.py
    ===================================================================
    --- lib/sqlalchemy/orm/properties.py    (revision 5124)
    +++ lib/sqlalchemy/orm/properties.py    (working copy)
    @@ -18,7 +18,7 @@
     from sqlalchemy.orm import (
         attributes, dependency, mapper, object_mapper, strategies,
         )
    -from sqlalchemy.orm.util import CascadeOptions, _class_to_mapper, _orm_annotate
    +from sqlalchemy.orm.util import CascadeOptions, _class_to_mapper, _orm_annotate, _configurational_column_spec
     from sqlalchemy.orm.interfaces import (
         MANYTOMANY, MANYTOONE, MapperProperty, ONETOMANY, PropComparator,
         StrategizedProperty,
    @@ -546,16 +546,12 @@
                 raise sa_exc.ArgumentError("relation '%s' expects a class or a mapper argument (received: %s)" % (self.key, type(self.argument)))
             assert isinstance(self.mapper, mapper.Mapper), self.mapper
    
    -        # accept callables for other attributes which may require deferred initialization
    -        for attr in ('order_by', 'primaryjoin', 'secondaryjoin', 'secondary', '_foreign_keys', 'remote_side'):
    -            if callable(getattr(self, attr)):
    -                setattr(self, attr, getattr(self, attr)())
    -        
    -        if self.order_by:
    -            self.order_by = [for x in util.to_list(self.order_by)](expression._literal_as_column(x))
    -        
    -        self._foreign_keys = set(expression._literal_as_column(x) for x in util.to_set(self._foreign_keys))
    -        self.remote_side = set(expression._literal_as_column(x) for x in util.to_set(self.remote_side))
    +        self.order_by = _configurational_column_spec(self.order_by, util.to_list)
    +        self.secondary = _configurational_column_spec(self.secondary)
    +        self.primaryjoin = _configurational_column_spec(self.primaryjoin)
    +        self.secondaryjoin = _configurational_column_spec(self.secondaryjoin)
    +        self._foreign_keys = _configurational_column_spec(self._foreign_keys, util.to_set)
    +        self.remote_side = _configurational_column_spec(self.remote_side, util.to_set)
    
             if not self.parent.concrete:
                 for inheriting in self.parent.iterate_to_root():
    Index: lib/sqlalchemy/orm/util.py
    ===================================================================
    --- lib/sqlalchemy/orm/util.py  (revision 5124)
    +++ lib/sqlalchemy/orm/util.py  (working copy)
    @@ -459,6 +459,28 @@
     def _state_mapper(state):
         return state.manager.mapper
    
    +def _configurational_column_spec(expr, filter=None):
    +    """Perform argument conversions on a column-based configurational expression."""
    +
    +    # interpret callables passed by declarative
    +    if callable(expr):
    +        expr = expr()
    +    
    +    # "False" is our current "use the default" symbol
    +    if expr is False:
    +        return expr
    +    
    +    if expr is None:
    +        ret = None
    +    elif isinstance(expr, (tuple, list, set)):
    +        ret = [for x in expr](_configurational_column_spec(x))
    +    else:
    +        
    +        ret = expression._literal_as_column(expr)
    +    if filter:
    +        ret = filter(ret)
    +    return ret
    +
     def object_mapper(object, raiseerror=True):
         """Given an object, return the primary Mapper associated with the object instance.
    
    Index: lib/sqlalchemy/orm/collections.py
    ===================================================================
    --- lib/sqlalchemy/orm/collections.py   (revision 5124)
    +++ lib/sqlalchemy/orm/collections.py   (working copy)
    @@ -105,9 +105,9 @@
    
     import sqlalchemy.exceptions as sa_exc
     from sqlalchemy import schema
    +from sqlalchemy.sql import expression
     import sqlalchemy.util as sautil
    
    -
     __all__ = ['collection_adapter',
                'mapped_collection', 'column_mapped_collection',
                'attribute_mapped_collection']('collection',)
    @@ -127,9 +127,12 @@
         after a session flush.
    
         """
    -    from sqlalchemy.orm.util import _state_mapper
    +    from sqlalchemy.orm.util import _state_mapper, _configurational_column_spec
         from sqlalchemy.orm.attributes import instance_state
    
    +
    +    mapping_spec = _configurational_column_spec(mapping_spec)
    +    
         if isinstance(mapping_spec, schema.Column):
             def keyfunc(value):
                 state = instance_state(value)
    
  3. Log in to comment