From d11b0cabaaf681fc8efa1afee705e1ee940a9cd2 Mon Sep 17 00:00:00 2001 From: kavore <161734431+kavore@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:25:53 +0300 Subject: [PATCH] fix(users): handle active discounts on delete Ensure active_discounts cascades on user/promo removal, clean orphan rows during migration, and rely on DB cascade for user deletion. --- db/migrator.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ db/models.py | 12 ++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/db/migrator.py b/db/migrator.py index e385f3b..18bb209 100644 --- a/db/migrator.py +++ b/db/migrator.py @@ -168,6 +168,48 @@ def _migration_0004_add_discount_promo_codes(connection: Connection) -> None: ) ) + +def _migration_0005_fix_active_discounts_fk_cascade(connection: Connection) -> None: + inspector = inspect(connection) + if not inspector.has_table("active_discounts"): + return + + connection.execute( + text( + "DELETE FROM active_discounts ad " + "WHERE NOT EXISTS (SELECT 1 FROM users u WHERE u.user_id = ad.user_id) " + "OR NOT EXISTS (SELECT 1 FROM promo_codes p WHERE p.promo_code_id = ad.promo_code_id)" + ) + ) + + connection.execute( + text("ALTER TABLE active_discounts DROP CONSTRAINT IF EXISTS active_discounts_user_id_fkey") + ) + connection.execute( + text("ALTER TABLE active_discounts DROP CONSTRAINT IF EXISTS fk_active_discounts_user") + ) + connection.execute( + text("ALTER TABLE active_discounts DROP CONSTRAINT IF EXISTS active_discounts_promo_code_id_fkey") + ) + connection.execute( + text("ALTER TABLE active_discounts DROP CONSTRAINT IF EXISTS fk_active_discounts_promo_code") + ) + + connection.execute( + text( + "ALTER TABLE active_discounts " + "ADD CONSTRAINT fk_active_discounts_user " + "FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE" + ) + ) + connection.execute( + text( + "ALTER TABLE active_discounts " + "ADD CONSTRAINT fk_active_discounts_promo_code " + "FOREIGN KEY (promo_code_id) REFERENCES promo_codes (promo_code_id) ON DELETE CASCADE" + ) + ) + MIGRATIONS: List[Migration] = [ Migration( id="0001_add_channel_subscription_fields", @@ -189,6 +231,11 @@ MIGRATIONS: List[Migration] = [ description="Add support for percentage discount promo codes", upgrade=_migration_0004_add_discount_promo_codes, ), + Migration( + id="0005_fix_active_discounts_fk_cascade", + description="Ensure active_discounts FKs cascade on user/promo delete", + upgrade=_migration_0005_fix_active_discounts_fk_cascade, + ), ] diff --git a/db/models.py b/db/models.py index 5547f20..cc55132 100644 --- a/db/models.py +++ b/db/models.py @@ -210,8 +210,16 @@ class ActiveDiscount(Base): """Tracks pending discount promo codes awaiting payment (permanent until used)""" __tablename__ = "active_discounts" - user_id = Column(BigInteger, ForeignKey("users.user_id"), primary_key=True) - promo_code_id = Column(Integer, ForeignKey("promo_codes.promo_code_id"), nullable=False) + user_id = Column( + BigInteger, + ForeignKey("users.user_id", ondelete="CASCADE"), + primary_key=True, + ) + promo_code_id = Column( + Integer, + ForeignKey("promo_codes.promo_code_id", ondelete="CASCADE"), + nullable=False, + ) discount_percentage = Column(Integer, nullable=False) activated_at = Column(DateTime(timezone=True), server_default=func.now())