From 083936d036b4444102f730e43e8ab67663c10d52 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 10 Apr 2026 17:11:43 +0530 Subject: [PATCH] feat: introduce public_id UUID field for user identification in URLs and APIs --- .../users/migrations/0003_user_public_id.py | 19 +++++++++++++++++++ .../migrations/0004_alter_user_public_id.py | 19 +++++++++++++++++++ backend/users/models.py | 6 ++++++ backend/users/serializers.py | 2 +- backend/users/utils.py | 2 +- backend/users/views.py | 2 +- 6 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 backend/users/migrations/0003_user_public_id.py create mode 100644 backend/users/migrations/0004_alter_user_public_id.py diff --git a/backend/users/migrations/0003_user_public_id.py b/backend/users/migrations/0003_user_public_id.py new file mode 100644 index 0000000..1c65723 --- /dev/null +++ b/backend/users/migrations/0003_user_public_id.py @@ -0,0 +1,19 @@ +# Generated by Django 6.0.4 on 2026-04-10 11:39 + +import uuid + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0002_user_kdf_salt_alter_user_is_active"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="public_id", + field=models.UUIDField(default=uuid.uuid4, editable=False, null=True), + ), + ] diff --git a/backend/users/migrations/0004_alter_user_public_id.py b/backend/users/migrations/0004_alter_user_public_id.py new file mode 100644 index 0000000..b3f0502 --- /dev/null +++ b/backend/users/migrations/0004_alter_user_public_id.py @@ -0,0 +1,19 @@ +# Generated by Django 6.0.4 on 2026-04-10 11:39 + +import uuid + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0003_user_public_id"), + ] + + operations = [ + migrations.AlterField( + model_name="user", + name="public_id", + field=models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, unique=True), + ), + ] diff --git a/backend/users/models.py b/backend/users/models.py index fbdd650..c5e16da 100644 --- a/backend/users/models.py +++ b/backend/users/models.py @@ -1,3 +1,5 @@ +import uuid + from django.contrib.auth.models import AbstractUser, BaseUserManager from django.db import models from django.utils.translation import gettext_lazy as _ @@ -32,8 +34,12 @@ class CustomUserManager(BaseUserManager): class User(AbstractUser): """ Database table structure. + Note: We use the default integer ID internally for database performance (joins/indexes) + but expose a random 'public_id' (UUID) in URLs and APIs for real privacy. """ + public_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, db_index=True) + # Reset default fields username = None first_name = None diff --git a/backend/users/serializers.py b/backend/users/serializers.py index 5242eda..d7c5660 100644 --- a/backend/users/serializers.py +++ b/backend/users/serializers.py @@ -9,7 +9,7 @@ class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ("id", "email", "full_name", "password") + fields = ("public_id", "email", "full_name", "password") def create(self, validated_data): user = User.objects.create_user( diff --git a/backend/users/utils.py b/backend/users/utils.py index b464f3d..4fabd00 100644 --- a/backend/users/utils.py +++ b/backend/users/utils.py @@ -7,7 +7,7 @@ from django.utils.http import urlsafe_base64_encode def send_activation_email(user): token = default_token_generator.make_token(user) - uid = urlsafe_base64_encode(force_bytes(user.pk)) + uid = urlsafe_base64_encode(force_bytes(user.public_id)) activation_url = f"{settings.FRONTEND_URL}/activate/{uid}/{token}" subject = "Activate Your Piku Account" message = f"""Hi {user.full_name}, diff --git a/backend/users/views.py b/backend/users/views.py index df9492c..5b53607 100644 --- a/backend/users/views.py +++ b/backend/users/views.py @@ -31,7 +31,7 @@ class ActivationView(generics.GenericAPIView): def get(self, request, uidb64, token): try: uid = urlsafe_base64_decode(uidb64).decode() - user = User.objects.get(pk=uid) + user = User.objects.get(public_id=uid) except (User.DoesNotExist, TypeError, ValueError): return Response({"detail": "Invalid activation link: User Error"}, status=status.HTTP_400_BAD_REQUEST) # validate token