Issue 1:
Migrated issue, originally created by Michael Bayer (@zzzeek)

this is related to #3365 but is a more fundamental change.

First, test case from #3365:

from sqlalchemy import Column, Integer, Text, ForeignKey, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, relationship

Base = declarative_base()

class Parent(Base):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)

class Child(Base):
__tablename__ = "child"
_id_parent = Column(
"id_parent", Integer, ForeignKey(Parent.id), primary_key=True)
name = Column(Text, primary_key=True)
parent = relationship(Parent)

engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
session = Session(engine)

p = Parent(id=1)
session.add(p)
session.commit()

c = Child(name="foo", parent=p)
session.add(c)
session.commit()

session.query(Child).filter(Child.parent == p).delete("evaluate")

raises: "AttributeError: 'Child' object has no attribute 'id_parent'"

with the fix, raises: "sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python. Specify 'fetch' or False for the synchronize_session parameter."

the patch involves adding ORM context to the primaryjoin/secondaryjoin:

diff --git a/lib/sqlalchemy/orm/evaluator.py b/lib/sqlalchemy/orm/evaluator.py
index 1e828ff..fb59186 100644
--- a/lib/sqlalchemy/orm/evaluator.py
+++ b/lib/sqlalchemy/orm/evaluator.py
@@ -59,7 +59,9 @@ class EvaluatorCompiler(object):
)
key = parentmapper._columntoproperty[clause].key
else:
- key = clause.key
+ raise UnevaluatableError(
+ "Cannot evaluate column: %s" % clause
+ )

get_corresponding_attr = operator.attrgetter(key)
return lambda obj: get_corresponding_attr(obj)
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index b649c9e..291af09 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -1942,6 +1942,7 @@ class JoinCondition(object):
self._annotate_fks()
self._annotate_remote()
self._annotate_local()
+ self._annotate_parentmapper()
self._setup_pairs()
self._check_foreign_cols(self.primaryjoin, True)
if self.secondaryjoin is not None:
@@ -2405,6 +2406,19 @@ class JoinCondition(object):
self.primaryjoin, {}, locals_
)

+ def _annotate_parentmapper(self):
+ if self.prop is None:
+ return
+
+ def parentmappers_(elem):
+ if "remote" in elem._annotations:
+ return elem._annotate({"parentmapper": self.prop.mapper})
+ elif "local" in elem._annotations:
+ return elem._annotate({"parentmapper": self.prop.parent})
+ self.primaryjoin = visitors.replacement_traverse(
+ self.primaryjoin, {}, parentmappers_
+ )
+
def _check_remote_side(self):
if not self.local_remote_pairs:
raise sa_exc.ArgumentError(
@@ -2811,9 +2825,6 @@ class JoinCondition(object):

bind_to_col = dict((binds[col].key, col) for col in binds)

- # this is probably not necessary
- lazywhere = _deep_deannotate(lazywhere)
-
return lazywhere, bind_to_col, equated_columns

We'd just start tracking "parentmapper" throughout all PJ/SJ conditions.

Issue 2:
I have this code.

def save_all_allowed_fields():
AllowedFieldsModel.query.delete()
db.session.commit()

db.session.add_all(set(
AllowedFieldsModel(field_name=n)
for n in request.json.get('fields')
))

db.session.commit()

return '', 204

and get error

sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) duplicate key value violates unique constraint "allowed_fields_pkey"
DETAIL: Key (field_name)=(profileType) already exists.

Way it is heppens if I deleted all rows?

version of alchemy 1.2.15, python 3.7