我在父母和孩子之间存在复杂的关系。 父母可以有很多孩子,但是只能访问一个未被遗弃的孩子。 孩子只能通过父级来访问,并且只有在其 is_abandoned 属性设置为 False 时才能访问。 放弃 Child 既不应删除 Child 条目,也不应更改其 parent_id ForeignKey值。
设置:
from pprint import pprint
from sqlalchemy import create_engine, inspect
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
BaseModelEntry = declarative_base()
class Parent(BaseModelEntry):
__tablename__ = 'parent'
id = Column(String(50), primary_key=True)
child = relationship(
"Child",
uselist=False,
primaryjoin="and_(Parent.id==Child.parent_id, Child.is_abandoned==False)"
)
def dict(self):
out = {c.name: getattr(self, c.name) for c in self.__table__.columns}
out.update(child=self.child.dict() if self.child else None)
return out
class Child(BaseModelEntry):
__tablename__ = 'child'
id = Column(Integer, primary_key=True, autoincrement=True)
parent_id = Column(String(50), ForeignKey("parent.id"))
is_abandoned = Column(Boolean)
def dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
engine = create_engine('sqlite:///')
_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
session = _session()
BaseModelEntry.metadata.create_all(bind=engine)
def put(parent_obj, session):
entry = session.merge(parent_obj)
session.commit()
print('commited entry')
pprint(entry.dict())
return entry
def _map_nested_models(model_data: dict):
for relation in inspect(Parent).relationships:
relation_cls = relation.mapper.class_
if model_data.get(relation.key):
model_data[relation.key] = relation_cls(**model_data[relation.key])
将现有的父项条目作为具有所有列和关系值的字典,我使用新数据执行自定义更新,并保留 parent ['child'] 数据(例如 id )和
parent['child']['is_abandoned'] = True
我在另一个会话中合并了此类数据。
# I create a parent with a not abandoned child
parent = Parent(id='123')
parent.child = Child(is_abandoned=False)
entry = put(parent, session)
print('child ', entry.child.id, 'is abandoned', entry.child.is_abandoned)
print('parent')
entry_dict = entry.dict()
pprint(entry_dict)
# within the same session everything works fine, but I want to isolate sessions so I create a new one
_session.remove()
session = _session()
print('abandoning')
entry.child.is_abandoned = True
print('updated parent')
updated_parent = entry_dict # as raw dict
pprint(updated_parent)
print('modeling') # only converts the nested dicts to the SQLAlchemy objects
_map_nested_models(updated_parent)
print(updated_parent)
updated_parent = Parent(**updated_parent)
new_entry = put(updated_parent, session)
print('\n')
print('child is', new_entry.child, ', should be None!') # but its not!
print('and from the direct query -', session.query(Parent).one().child, ', it should be None as well') # but its not!
print('its really', session.query(Child).one().is_abandoned, 'that it is abandoned') # False!
_session.remove()
这种行为的可能原因是什么?如何在始终遵守 primaryjoin 约束的情况下更改它以获得结果?