从SQLAlchemy会话中删除对象

13 投票
3 回答
20919 浏览
提问于 2025-04-16 04:45

我想把一个映射类的实例传递给一个不认识SQLAlchemy的方法(在另一个进程中),而我只需要我的属性值。问题是,每当这个方法想读取属性值时,就会出现一个UnboundExecutionError的错误。我明白为什么会这样,但我希望能找到解决这个问题的方法。

我只需要我定义的属性值(在这个例子中是idnamedirty),而不需要在目标方法中使用SQLAlchemy的那些复杂功能。

示例类:

Base = declarative_base()
class Record(Base):
    __tablename__ = 'records'
    _id = Column('id', Integer, primary_key=True)
    _name = Column('name', String(50))
    _dirty = Column('dirty', Boolean, index=True)

    @synonym_for('_id')
    @property
    def id(self):
        return self._id

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        self._dirty = True

    name = synonym('_name', descriptor=name)

    @synonym_for('_dirty')
    @property
    def dirty(self):
        return self._dirty

示例调用:

...
def do_it(self):
    records = self.query.filter(Record.dirty == True)
    for record in records:
        pass_to_other_process(record)

我尝试过使用session.expunge()copy.copy(),但都没有成功。

3 个回答

0

你可能需要把这个对象变成临时状态

这描述了一个对象在会话中可能拥有的主要状态之一;临时对象是一个新的对象,它没有任何数据库身份,也还没有和会话关联。当这个对象被添加到会话中时,它会变成待处理状态。

你可以通过删除旧对象,将其变为临时状态,然后用它来创建新对象。

类似于:

# Get the original object (new query style)
query = select(MyTable).where(MyTable.id == old_object_id)
results = session.execute(query)
old_object = result.scalar_one_or_none()

# Remove it from the session & make it transient
session.expunge(old_object)
make_transient(old_object)

# Use it to create the new object
new_object = MyTable(attr1=old_object.attr1 ...)

# Add & commit the new object
session.add(new_object)
session.commit()

20

你需要把SQL Alchemy对象从会话中移除,也就是所谓的“清除”。这样你就可以请求任何已经加载的属性,而不会让它试图重新使用它最后已知的会话或未绑定的会话。

self.expunge(record)

不过要注意,任何未加载的属性会返回它最后已知的值或者是None。如果你想以后再使用这个对象,可以再次调用“添加”或者“合并”。

self.add(record)
9

我猜测你遇到了SQLAlchemy的懒加载问题。因为我对SQLAlchemy的内部机制了解不多,所以我建议你这样做:

class RecordData(object):
    __slots__ = ('id', 'name', 'dirty')

    def __init__(self, rec):
        self.id = rec.id
        self.name = rec.name
        self.dirty = rec.dirty

然后稍后...

def do_it(self):
    records = self.query.filter(Record.dirty == True)
    for record in records:
        pass_to_other_process(RecordData(record))

现在,我觉得有一种方法可以告诉SQLAlchemy把你的对象变成一个“傻乎乎”的对象,这个对象和数据库没有任何连接,看起来和我刚才做的很像。不过我不知道具体是什么方法。

撰写回答