Pourquoi une requête appeler une auto-flush dans SQLAlchemy?

Le code que vous voyez ci-dessus est juste un exemple, mais il fonctionne à reproduire cette erreur:

sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; 
consider using a session.no_autoflush block if this flush is occurring prematurely)
(sqlite3.IntegrityError) NOT NULL constraint failed: X.nn 
[SQL: 'INSERT INTO "X" (nn, val) VALUES (?, ?)'] [parameters: (None, 1)]

Une instance mappée est tout de même ajouté une session. L'instance veut savoir (ce qui signifie la requête sur la base de données) si d'autres instances de son propre type existe avoir les mêmes valeurs. Il y a un deuxième attribut/colonne (_nn). Il est alors précisé à NOT NULL. Mais par défaut il est NULL.

Lorsque l'instance (comme dans l'exemple) est toujours ajouté à la session de l'appel à query.one() appeler une auto-flush. Rincer créer un INSERT qui tente de stocker l'instance. Cela échoue, car _nn est toujours nulle et viole les NOT NULL contrainte.

Qu'est ce que je comprends actuellement.
Mais la question est pourquoi est-il invoquer une auto-flush? Puis-je bloquer?

#!/usr/bin/env python3

import os.path
import os
import sqlalchemy as sa 
import sqlalchemy.orm as sao
import sqlalchemy.ext.declarative as sad
from sqlalchemy_utils import create_database

_Base = sad.declarative_base()
session = None


class X(_Base):
    __tablename__ = 'X'

    _oid = sa.Column('oid', sa.Integer, primary_key=True)
    _nn = sa.Column('nn', sa.Integer, nullable=False) # NOT NULL!
    _val = sa.Column('val', sa.Integer)

    def __init__(self, val):
        self._val = val

    def test(self, session):
        q = session.query(X).filter(X._val == self._val)
        x = q.one()
        print('x={}'.format(x))

dbfile = 'x.db'

def _create_database():
    if os.path.exists(dbfile):
        os.remove(dbfile)

    engine = sa.create_engine('sqlite:///{}'.format(dbfile), echo=True)
    create_database(engine.url)
    _Base.metadata.create_all(engine)
    return sao.sessionmaker(bind=engine)()


if __name__ == '__main__':
    session = _create_database()

    for val in range(3):
        x = X(val)
        x._nn = 0
        session.add(x)
    session.commit()

    x = X(1)
    session.add(x)
    x.test(session)

Bien sûr, une solution serait de pas ajouter une instance de la séance, avant de query.one() a été appelé. De ce travail. Mais dans ma vraie (mais complexe pour cette question) de cas d'utilisation, il n'est pas une solution sympa.

OriginalL'auteur buhtz | 2015-10-03