I have encountered a strange problem when using SQLAlchemy and hoping that this community can help.
So the story is as follows:
- I have custom classes, where interesting thing is that Forecast class is part of Job class (job should have list of Forecasts inside). They are defined like that:
class Job(db.Model):
__tablename__ = 'job'
id = db.Column(db.Integer(), primary_key=True)
job_code = db.Column(db.String(4), nullable=True)
job_description = db.Column(db.String(200), nullable=False)
forecasts = db.relationship('Forecast', order_by="Forecast.time", cascade="all, delete")
class Forecast(db.Model):
__tablename__ = 'revenue'
id = db.Column(db.Integer, primary_key=True)
job_id = db.Column(db.Integer, db.ForeignKey('forecast_job.id'))
time = db.Column(db.String(7), nullable=False)
forecast = db.Column(db.Integer, nullable=False)
- It works all fine untill this moment. In my code I have option to join 2 jobs together
# Open session to run operation
engine = create_engine(current_app.config["SQLALCHEMY_DATABASE_URI"])
Session = sessionmaker(bind = engine)
mer_session = Session()
# Select jobs
old_forecast = mer_session.query(Job).filter(Job.id == manual_id).first()
new_forecast = mer_session.query(Job).filter(Job.id == auto_id).first()
# Copy financial forecast from manual record to automatic one
new_forecast.forecasts = old_forecast.forecasts
# When we do comitt later on it should be all updated
# Delete record manually
mer_session.delete(old_forecast)
#Commit changes
mer_session.commit()
- This is critical line:
new_forecast.forecasts = old_forecast.forecasts
Where new_forecast.forecasts is empty then it works fine (so I replace no values with new ones). However, when I need to overwrite some old info (new_forecast.forecasts is non empty) then I get the following error:
cursor.executemany(statement, parameters)
sqlalchemy.exc.IntegrityError: (pyodbc.IntegrityError) ('23000', "[23000] [Microsoft][ODBC SQL Server Driver][SQL Server]Cannot insert the value NULL into column 'job_id', table '....dbo.revenue'; column does not allow nulls. UPDATE fails. (515) (SQLExecDirectW); [23000] [Microsoft][ODBC SQL Server Driver][SQL Server]The statement has been terminated. (3621)")
So obviously SQLAlchemy mixes something in Forecast when tryint to add records with emtpy job_id. How to make SQLAlchemy perfor replacement in a way:
- delete new_forecast.forecasts
- copy old_forecast.forecasts into new_forecast.forecasts
- commit changes
I can do it step by step but this is so silly that there must be some automatic way via functinality of SQLAlchemy.