Le code que vous voyez ci-dessus n'est qu'un exemple mais il fonctionne pour 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 toujours ajoutée à une session. L'instance veut savoir (ce qui signifie requête sur la base de données) si d'autres instances son propre type existe avec les mêmes valeurs. Il y a un deuxième attribut/colonne (_nn
). Il est spécifié à NOT NULL
. Mais par défaut, c'est NULL
.
Lorsque l'instance (comme dans l'exemple) est toujours ajoutée à la session, un appel à query.one()
appelle un vidage automatique. Cette vidange crée un INSERT
qui essaie de stocker l'instance. Cela échoue car _nn
Est toujours nul et viole la contrainte NOT NULL
.
C'est ce que je comprends actuellement. Mais la question est pourquoi appelle-t-elle un rinçage automatique? Puis-je bloquer cela?
#!/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 l'instance à la session avant l'appel de query.one()
. Ce travail. Mais dans mon cas d'utilisation réel (mais trop complexe pour cette question), ce n'est pas une bonne solution.
Comment désactiver la fonction autoflush :
Temporaire: vous pouvez utiliser no_autoflush gestionnaire de contexte sur l'extrait de code où vous interrogez la base de données, c'est-à-dire dans X.test
méthode:
def test(self, session):
with session.no_autoflush:
q = session.query(X).filter(X._val == self._val)
x = q.one()
print('x={}'.format(x))
À l'échelle de la session: passez simplement autoflush=False
à votre sessionmaker:
return sao.sessionmaker(bind=engine, autoflush=False)()