From d5b7d79ca6206c27ad6e6f22dc418262a3699f10 Mon Sep 17 00:00:00 2001 From: Jaideep Date: Fri, 6 Feb 2026 06:20:14 +0530 Subject: [PATCH] Add unique constraint to Subscribers email field - Add unique=True to Subscribers.email field in models - Create migration with data cleanup for existing duplicates - Migration removes duplicate entries keeping oldest by created_at - Prevents race conditions in concurrent subscription requests - Ensures data integrity at database level Fixes #4865 --- ..._unique_constraint_to_subscribers_email.py | 50 +++++++++++++++++++ apps/web/models.py | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 apps/web/migrations/0011_add_unique_constraint_to_subscribers_email.py diff --git a/apps/web/migrations/0011_add_unique_constraint_to_subscribers_email.py b/apps/web/migrations/0011_add_unique_constraint_to_subscribers_email.py new file mode 100644 index 0000000000..c5c560f3b4 --- /dev/null +++ b/apps/web/migrations/0011_add_unique_constraint_to_subscribers_email.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 2.2.20 on 2026-02-06 00:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +def remove_duplicate_subscribers(apps, schema_editor): + """ + Remove duplicate subscriber entries before adding unique constraint. + Keep only the earliest entry for each email address. + """ + Subscribers = apps.get_model("web", "Subscribers") + db_alias = schema_editor.connection.alias + + # Get all emails that have duplicates + from django.db.models import Count + duplicate_emails = ( + Subscribers.objects.using(db_alias) + .values("email") + .annotate(count=Count("id")) + .filter(count__gt=1) + ) + + # For each duplicate email, keep the oldest entry and delete the rest + for item in duplicate_emails: + email = item["email"] + subscribers = Subscribers.objects.using(db_alias).filter(email=email).order_by("created_at") + # Keep the first (oldest) entry, delete the rest + for subscriber in subscribers[1:]: + subscriber.delete() + + +class Migration(migrations.Migration): + + dependencies = [("web", "0010_add_verbose_name_plural_for_subscribers_model")] + + operations = [ + # First, remove any duplicate entries + migrations.RunPython( + remove_duplicate_subscribers, + reverse_code=migrations.RunPython.noop, + ), + # Then add the unique constraint + migrations.AlterField( + model_name="subscribers", + name="email", + field=models.EmailField(max_length=70, unique=True), + ), + ] diff --git a/apps/web/models.py b/apps/web/models.py index d0f3aca10b..efd73639c3 100644 --- a/apps/web/models.py +++ b/apps/web/models.py @@ -29,7 +29,7 @@ class Meta: class Subscribers(TimeStampedModel): """Model representing subbscribed user's email""" - email = models.EmailField(max_length=70) + email = models.EmailField(max_length=70, unique=True) def __str__(self): return "{}".format(self.email)