SQLAlchemy 会话在实例化纯 python 模型时以某种方式隐式创建条目

SQLAlchemy session somehow implicitly creates entry when instantiating pure python model

我发现 SQLAlchemy 有一些我意想不到的行为。在 many-to-many 关系的特定场景中,即使从未调用 add() 方法,也会将条目添加到 session 对象。

代码:

# orm.py
metadata = MetaData()


class Student:
    id: int
    classes: list

    def __init__(self, classes: list, id: int = None):
        self.id = id
        self.classes = classes


class Class:
    id: int
    students: list

    def __init__(self, students: list, id: int = None):
        self.id = id
        self.students = students


students = Table(
    'students',
    metadata,
    Column('id', Integer, primary_key=True),
)

classes = Table(
    'classes',
    metadata,
    Column('id', Integer, primary_key=True)
)

student_classes = Table(
    'student_classes',
    metadata,
    Column('student_id', Integer, ForeignKey('students.id')),
    Column('class_id', Integer, ForeignKey('classes.id')),
    UniqueConstraint('student_id', 'class_id'),
)


def start_mappers():
    mapper(Student, students, properties={
        'classes': relationship(Class, secondary=student_classes, back_populates='students')
    })
    mapper(Class, classes, properties={
        'students': relationship(Student, secondary=student_classes, back_populates='classes')
    })
# test.py

class Test(unittest.TestCase):


    def test_adding_students_and_classes(self):
        engine = create_engine('sqlite:///:memory:')
        metadata.create_all(engine)
        session_factory = sessionmaker(bind=engine)
        start_mappers()
        session = session_factory()
        student = Student(classes=[])
        session.add(student)

        a_class = Class(students=[student])
        self.assertEqual(session.query(Class).count(), 0) # fails because count() = 1

上面的测试失败了,因为创建一个纯 python 模型的 Class 实例会以某种方式隐式触发一个条目添加到会话中。

我很困惑,因为我希望会话只会在显式调用文档中特定的方法之一时发生变化,例如 add()delete().

此外,我很困惑 SQLAlchemy 是如何意识到我正在创建 Class 的实例的。有某种听众吗?

调用mapper(Class, ...) 工具Class,所以它不再“纯粹”了。定义的关系似乎使用默认 cascade configuration, which includes save-update。在这种情况下,因为 Student 实例已添加到会话中,它也会通过其 students 关系在会话中拉取 Class 实例。默认情况下,自动刷新在每个查询之前启动,并且由于 Class 处于待处理状态,因此它会在

之前持久化
session.query(Class).count()

是运行。