Django Rest Framework对接Android发送邮件时重复创建用户问题
Hey there! Let's break down what's causing that duplicate user issue and fix it step by step.
The Root Cause
When you enable send_mail() in your view, it runs synchronously—meaning your server pauses to send the email before sending a response back to the Android app. If the email takes too long to send, the Android client might time out and retry the registration request. This leads to two identical requests hitting your server, creating duplicate users (and triggering the "user already exists" error). Postman doesn't have this problem because it waits longer for responses, so it doesn't retry.
Fix 1: Send Emails Asynchronously (Most Critical)
The biggest fix is to offload email sending to a background task so your view can respond immediately to the Android app, preventing retries. Here are two easy ways to do this:
Option A: Use Threading (Quick & Simple)
Wrap the send_mail call in a thread to let it run in the background:
# In your views.py import threading from django.core.mail import send_mail def send_activation_email(): send_mail('mail_subject', 'message','test.com <no-reply@test.com>', ['test@gmail.com']) # Inside the create method if not social_acc == "Yes": # Start the email in a separate thread threading.Thread(target=send_activation_email).start() return Response( {'code': 'reg_success', 'message': 'User created successfully. Please confirm your email'}, status=HTTP_201_CREATED )
Option B: Use Celery (Scalable for Production)
For production apps, Celery is a better choice for background tasks. First set up Celery with your Django project, then create a task:
# tasks.py from celery import shared_task from django.core.mail import send_mail @shared_task def send_activation_email(): send_mail('mail_subject', 'message','test.com <no-reply@test.com>', ['test@gmail.com'])
Then call the task asynchronously in your view:
# views.py from .tasks import send_activation_email # Inside the create method if not social_acc == "Yes": send_activation_email.delay() # Async call return Response(...)
Fix 2: Add Atomic Transactions to Prevent Duplicates
Even if the app retries, we can make sure the database doesn't create duplicate users. Your current check-then-create logic isn't atomic—two requests could pass the existence check at the same time before either creates a user. Wrap the logic in a transaction and handle database integrity errors:
from django.db import transaction, IntegrityError def create(self, request, *args, **kwargs): data = request.data serializer = self.get_serializer(data=data) try: with transaction.atomic(): if serializer.is_valid(): self.perform_create(serializer) social_acc = data.get('social_acc') if not social_acc == "Yes": send_activation_email.delay() # Use async email here return Response( {'code': 'reg_success', 'message': 'User created successfully. Please confirm your email'}, status=HTTP_201_CREATED ) return Response( {'code': 'reg_success', 'message': 'User created successfully.'}, status=HTTP_201_CREATED ) except IntegrityError: # Check which field caused the duplicate if UserModel.objects.filter(username=data.get("username")).exists(): return Response({'code': 'reg_fail', 'message': 'Username already exists.'}) if UserModel.objects.filter(email=data.get("email")).exists(): return Response({'code': 'reg_fail', 'message': 'Current email has already been used.'}) return Response({'code': 'reg_fail', 'message': 'Please check your data.'})
This leverages your User model's unique=True constraints on username and email, and catches the database error if a duplicate is attempted.
Fix 3: Optimize Android Client Behavior
Work with your Android team to:
- Set a reasonable timeout for registration requests (long enough to wait for a response without retrying prematurely)
- Disable retries for registration requests, or add a unique request ID (like a UUID) to avoid processing duplicate requests on the server
Extra Check
Your post_save signal for creating ActivateUser looks fine (the try-except prevents it from breaking user creation if something goes wrong), but double-check that your serializer is handling all fields correctly—especially social_acc—to avoid unexpected validation delays.
内容的提问来源于stack exchange,提问作者Antef Technologies

