πŸ“ Ending Product Sales Without Breaking Sales History β€” Implementing Soft Deletes and Reducing Gemini API Usage in Flask A developer building a bakery sales management application with Flask, PostgreSQL, and the Gemini API implemented soft deletes to preserve sales history when editing product masters, and reduced Gemini API usage. The developer, a self-taught programmer with about 135 hours of study, encountered a NotNullViolation error when deleting products referenced by sales records, and solved it by updating existing products by primary key and inserting only new ones. The update also cut Gemini API calls by optimizing the flow. Hello from Japan πŸ‡―πŸ‡΅ This article was originally published in Japanese on Qiita and has been translated and adapted for DEV Community. I am a professional truck driver teaching myself Python while developing a bakery sales management application with Flask, PostgreSQL, and the Gemini API. At the time of this work, I had completed approximately 135 hours of programming study . I also made a major update to the application and its README. https://github.com/tosane932/sales data app https://github.com/tosane932/sales data app When I added product-master editing, I encountered several connected problems: I spent approximately six hours reviewing the entire flow: Product updates β†’ Product discontinuation β†’ Database migration β†’ Production startup β†’ Gemini API execution This article records the failed implementation and how I changed it into a safer structure. This is a long development record, so thank you for your patience. When I updated the product master, the following error occurred: psycopg2.errors.NotNullViolation: null value in column "product id" of relation "daily sales" violates not-null constraint SQLAlchemy attempted to execute an update similar to: UPDATE daily sales SET product id = NULL WHERE daily sales.id = ... The DailySales.product id column was defined with nullable=False . product id = db.Column db.Integer, db.ForeignKey "products.id" , nullable=False, Therefore, when I tried to delete a product connected to existing sales history, SQLAlchemy could not change the related product id to NULL . The original product-master update logic deleted all products for the selected year and month, then recreated them from the submitted form. Product.query.filter by year=year, month=month, .delete for product in products data: db.session.add Product year=year, month=month, name=product "name" , price=product "price" , db.session.commit This looks simple when considering only the product-master table. However, the daily-sales table references Product.id . products └── id = 1 daily sales └── product id = 1 Deleting and recreating the product generates a different primary key even when the visible product name is unchanged. Before deletion: White Bread, id = 1 After re-registration: White Bread, id = 15 To a human, both records represent the same product. To the database, they are completely different rows. I added each existing product's Product.id to the form as a hidden field.