summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/relationships.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-03-27 20:38:46 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-03-27 20:38:46 -0400
commit825d3b0d6db4b4db78d69b9dcf012c5f627385d3 (patch)
tree6f810b48a856c969b6c107f8b987e5b0686450b4 /lib/sqlalchemy/orm/relationships.py
parent0611baa889198421afa932f2af1524bd8826ed7d (diff)
downloadsqlalchemy-825d3b0d6db4b4db78d69b9dcf012c5f627385d3.tar.gz
- Fixed a very old behavior where the lazy load emitted for a one-to-many
could inappropriately pull in the parent table, and also return results inconsistent based on what's in the parent table, when the primaryjoin includes some kind of discriminator against the parent table, such as ``and_(parent.id == child.parent_id, parent.deleted == False)``. While this primaryjoin doesn't make that much sense for a one-to-many, it is slightly more common when applied to the many-to-one side, and the one-to-many comes as a result of a backref. Loading rows from ``child`` in this case would keep ``parent.deleted == False`` as is within the query, thereby yanking it into the FROM clause and doing a cartesian product. The new behavior will now substitute the value of the local "parent.deleted" for that parameter as is appropriate. Though typically, a real-world app probably wants to use a different primaryjoin for the o2m side in any case. fixes #2948
Diffstat (limited to 'lib/sqlalchemy/orm/relationships.py')
-rw-r--r--lib/sqlalchemy/orm/relationships.py15
1 files changed, 11 insertions, 4 deletions
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index a0b36ce57..311fba478 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -2587,6 +2587,7 @@ class JoinCondition(object):
binds = util.column_dict()
lookup = util.column_dict()
equated_columns = util.column_dict()
+ being_replaced = set()
if reverse_direction and self.secondaryjoin is None:
for l, r in self.local_remote_pairs:
@@ -2594,16 +2595,22 @@ class JoinCondition(object):
_list.append((r, l))
equated_columns[l] = r
else:
+ # replace all "local side" columns, which is
+ # anything that isn't marked "remote"
+ being_replaced.update(self.local_columns)
for l, r in self.local_remote_pairs:
_list = lookup.setdefault(l, [])
_list.append((l, r))
equated_columns[r] = l
def col_to_bind(col):
- if col in lookup:
- for tobind, equated in lookup[col]:
- if equated in binds:
- return None
+ if col in being_replaced or col in lookup:
+ if col in lookup:
+ for tobind, equated in lookup[col]:
+ if equated in binds:
+ return None
+ else:
+ assert not reverse_direction
if col not in binds:
binds[col] = sql.bindparam(
None, None, type_=col.type, unique=True)