Il codice che vedete sopra è solo un esempio, ma si lavora per riprodurre questo errore:Perché una query richiama uno svuotamento automatico in SQLAlchemy?
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)]
Un mappato istanza è ancora aggiunto a una sessione. L'istanza vuole sapere (che significa interrogare sul database) se esistono altre istanze del proprio tipo con gli stessi valori. C'è un secondo attributo/colonna (_nn
). È specificato a NOT NULL
. Ma per impostazione predefinita è NULL
.
Quando l'istanza (come nell'esempio) viene ancora aggiunta alla sessione, una chiamata a query.one()
richiama una procedura di svuotamento automatico. Questo colore crea uno INSERT
che tenta di archiviare l'istanza. Ciò non riesce perché _nn
è ancora null e viola il vincolo NOT NULL
.
Questo è quello che capisco attualmente. Ma la domanda è: perché richiama un auto-flush? Posso bloccarlo?
#!/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)
Naturalmente una soluzione potrebbe essere quella di non aggiungere l'istanza per la sessione prima query.one()
è stato chiamato. Questo lavoro. Ma nel mio caso d'uso reale (ma complesso per questa domanda) non è una buona soluzione.
Qualcuno può spiegare perché è necessario? –
@JonathanLeaders Nella risposta c'è un collegamento ai documenti in cui viene spiegata la funzione di autoflush. Se qualcosa non è chiaro, faresti meglio a porre una domanda specifica. – Palasaty
Si è scoperto che non stavo chiamando 'db.commit()' quando dovevo, quindi mi ha fatto pensare che 'autoflush' non funzionasse come previsto, quindi la mia domanda era basata su un errore. –