为什么在另一个会话中不尊重SQLAlchemy关系primaryjoin?

时间:2018-07-21 14:51:31

标签: python sqlalchemy

我在父母孩子之间存在复杂的关系。 父母可以有很多孩子,但是只能访问一个未被遗弃的孩子孩子只能通过父级来访问,并且只有在其 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 约束的情况下更改它以获得结果?

0 个答案:

没有答案