variable indicating their creation order, which
declarative_base() maintains when generating
Table constructs.
+
+ - Class-bound attributes sent as arguments to
+ relation()'s remote_side and foreign_keys parameters
+ are now accepted, allowing them to be used with
+ declarative (and therefore self-referential many-to-one
+ relations); merged from 0.5.
0.4.6
=====
from sqlalchemy import sql, schema, util, exceptions, logging
from sqlalchemy.sql.util import ClauseAdapter, criterion_as_pairs, find_columns
-from sqlalchemy.sql import visitors, operators, ColumnElement
+from sqlalchemy.sql import visitors, operators, ColumnElement, expression
from sqlalchemy.orm import mapper, sync, strategies, attributes, dependency, object_mapper
from sqlalchemy.orm import session as sessionlib
from sqlalchemy.orm.mapper import _class_to_mapper
if self._legacy_foreignkey and not self._refers_to_parent_table():
self.foreign_keys = self._legacy_foreignkey
- arg_foreign_keys = self.foreign_keys
+ arg_foreign_keys = set(expression._literal_as_column(x) for x in util.to_set(self.foreign_keys))
if self._arg_local_remote_pairs:
if not arg_foreign_keys:
else:
eq_pairs = self._arg_local_remote_pairs
elif self.remote_side:
+ remote_side = set(expression._literal_as_column(x) for x in util.to_set(self.remote_side))
+
if self.direction is MANYTOONE:
- eq_pairs = criterion_as_pairs(self.primaryjoin, consider_as_referenced_keys=self.remote_side, any_operator=True)
+ eq_pairs = criterion_as_pairs(self.primaryjoin, consider_as_referenced_keys=remote_side, any_operator=True)
else:
- eq_pairs = criterion_as_pairs(self.primaryjoin, consider_as_foreign_keys=self.remote_side, any_operator=True)
+ eq_pairs = criterion_as_pairs(self.primaryjoin, consider_as_foreign_keys=remote_side, any_operator=True)
else:
if self.viewonly:
eq_pairs = self.synchronize_pairs
assert User.addresses
assert mapperlib._new_mappers is False
+ def test_uncompiled_attributes_in_relation(self):
+ class Address(Base, Fixture):
+ __tablename__ = 'addresses'
+ id = Column(Integer, primary_key=True)
+ email = Column(String(50))
+ user_id = Column(Integer, ForeignKey('users.id'))
+
+ class User(Base, Fixture):
+ __tablename__ = 'users'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50))
+ addresses = relation("Address", order_by=Address.email,
+ foreign_keys=Address.user_id,
+ remote_side=Address.user_id,
+ )
+
+ # get the mapper for User. User mapper will compile,
+ # "addresses" relation will call upon Address.user_id for
+ # its clause element. Address.user_id is a _CompileOnAttr,
+ # which then calls class_mapper(Address). But ! We're already
+ # "in compilation", but class_mapper(Address) needs to initialize
+ # regardless, or COA's assertion fails
+ # and things generally go downhill from there.
+ class_mapper(User)
+
+ Base.metadata.create_all()
+
+ sess = create_session()
+ u1 = User(name='ed', addresses=[Address(email='abc'), Address(email='xyz'), Address(email='def')])
+ sess.save(u1)
+ sess.flush()
+ sess.clear()
+ self.assertEquals(sess.query(User).filter(User.name == 'ed').one(),
+ User(name='ed', addresses=[Address(email='abc'), Address(email='def'), Address(email='xyz')])
+ )
+
def test_nice_dependency_error(self):
class User(Base):
__tablename__ = 'users'