对旧对象使用新会话时,SQLAlchemy对象“已存在于该会话中”

2024-10-02 06:28:10 发布

您现在位置:Python中文网/ 问答频道 /正文

一些背景: 我有一组从基表继承的表,称为thingsthings有一个名为objecttypes的表的外键objecttypes为每个子表提供polymorphic_identity

我发现,当我创建一个新会话时,在某些情况下,旧会话中的数据不会合并到新会话中

如果我有:

eastGate = Gate(name="eastGate", origin=tokyo, destinationInput=luggage, objectType=typeDict['gate'])

其中origindestinationInputobjectType都是以前定义过对象的关系,我看到以下错误:

sqlalchemy.exc.InvalidRequestError: Can't attach instance <ObjectType at 0x10592fe50>; another instance with key (
<class '__main__.ObjectType'>, (1,), None) is already present in this session.

可能出现这种情况的情况有很多,但在这种特定情况下,情况如下:

  1. 将新对象添加到新会话中
  2. 关闭会话并从作用域为的会话中删除()
  3. 通过关系引用两个新构造的对象
  4. 错误出现在第二个对象上
    # typeDict just contains a pre-fetched ObjectTypes 
    tokyo = Location(name="tokyo", objectType=typeDict['location'])
    tokyo = write(tokyo)

    # If I clear out the current session here, the error will occur
    scopedSessionFactory().close()
    scopedSessionFactory.remove()

    westGate = Gate(name="westGate", destination=tokyo, objectType=typeDict['gate'])
    westGate = write(westGate)

    luggage = LocationInput(name="luggage", objectType=typeDict['locationinput'])
    luggage = write(luggage)

    # This is the line where the error occurs
    eastGate = Gate(name="eastGate", origin=tokyo, destinationInput=luggage, objectType=typeDict['gate'])
    eastGate = write(eastGate) 

现在我已经重现了这个问题,我不太确定根本原因是什么。在这个特定的例子中,我可以使用相同的会话,但我希望能够缩短一些会话的寿命

环境: Python:2.7.14(也发生在3.7.x上) SqlAlchemy:1.3.20

完整示例如下:

import sqlalchemy as sa
from sqlalchemy import create_engine, inspect, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, aliased, contains_eager, scoped_session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class ObjectType(Base):
    __tablename__ = "objecttypes"
    id=Column(Integer, primary_key=True)
    name=Column(String)

    def __repr__(self):
        return "ObjectType({}, id={})".format(self.name, self.id)

engine = create_engine('sqlite:///:memory:', echo=False)
Base.metadata.create_all(engine) # We'll call this again later
sessionFactory = sessionmaker(bind=engine, expire_on_commit=False)
scopedSessionFactory = scoped_session(sessionFactory)

def startScope():
    return scopedSessionFactory()

def endScope():
    scopedSessionFactory().close()
    scopedSessionFactory.remove()
    return

def addObjectTypes():
    """
    Add in all the object types to the db without using a session
    """
    values = ["('location')", "('locationinput')", "('gate')"]
    q =  """INSERT INTO objecttypes (name) VALUES {}""".format(",".join(values))
    engine.execute(q)
    return

def buildObjectTypes():
    addObjectTypes()
    session = startScope()
    types = session.query(ObjectType).all()
    endScope()
    return dict([(objType.name, objType) for objType in types])

# Holds all the types
typeDict = buildObjectTypes()

class Thing(Base):
    __tablename__ = "things"
    id=Column(Integer, primary_key=True)
    name=Column(String)
    object_type_id=Column(Integer, ForeignKey('objecttypes.id'))
    objectType=relationship(ObjectType)
    version=Column(Integer, nullable=False)
    timeCreated=Column(DateTime)

    __mapper_args__ = {
        'version_id_col': version,
        'polymorphic_on':object_type_id,
        'with_polymorphic':'*',
        'polymorphic_load':'inline',
    }

    def __repr__(self):
        return "{}, id={}, type={}, version={}".format(self.name, self.id, self.objectType, self.version)


class Location(Thing):
    __tablename__ = "locations"
    id=Column(Integer, ForeignKey('things.id'),primary_key=True)
    __mapper_args__ = {
        'polymorphic_identity': typeDict['location'].id
    }

class LocationInput(Thing):
    __tablename__ = "locationinputs"
    id=Column(Integer, ForeignKey('things.id'),primary_key=True)
    previousGateId=Column(Integer, ForeignKey('gates.id'))
    __mapper_args__ = {
        'polymorphic_identity': typeDict['locationinput'].id
    }

class Gate(Thing):
    __tablename__ = "gates"
    id=Column(Integer, ForeignKey('things.id'),primary_key=True)

    originId=Column(Integer, ForeignKey('locations.id'))
    origin=relationship(Location, foreign_keys=[originId], backref="originGates")

    originInputId=Column(Integer, ForeignKey('locationinputs.id'))
    originInput=relationship(LocationInput, foreign_keys=[originInputId], backref="originInputGates")

    destinationId=Column(Integer, ForeignKey('locations.id'))
    destination=relationship(Location, foreign_keys=[destinationId], backref="destinationGates")

    destinationInputId=Column(Integer, ForeignKey('locationinputs.id'))
    destinationInput=relationship(LocationInput, foreign_keys=[destinationInputId], backref="destinationInputGates")

    __mapper_args__ = {
        'polymorphic_identity': typeDict['gate'].id
    }

LocationInput.previousGate = relationship(Gate, foreign_keys=[LocationInput.previousGateId])
Base.metadata.create_all(engine)

def write(obj):
    session = scopedSessionFactory()
    return session.merge(obj)

def reproduceError():
    tokyo = Location(name="tokyo", objectType=typeDict['location'])
    tokyo = write(tokyo)

    # If I clear out the current session here, the error will occur
    scopedSessionFactory().close()
    scopedSessionFactory.remove()

    westGate = Gate(name="westGate", destination=tokyo, objectType=typeDict['gate'])
    westGate = write(westGate)

    luggage = LocationInput(name="luggage", objectType=typeDict['locationinput'])
    luggage = write(luggage)

    # This is the line where the error occurs
    eastGate = Gate(name="eastGate", origin=tokyo, destinationInput=luggage, objectType=typeDict['gate'])
    eastGate = write(eastGate)
    print(eastGate)
    return

reproduceError()


Tags: thenameidsessioncolumnintegerwriteforeignkey

热门问题