I’m building an authentication flow that takes a username and email. The user then receives an email with a confirmation link to complete their registration, which currently just involves setting a password. Here’s what I’m doing:
- When the user submits their username and email, an account is created with
is_active = False. - Once they set a password, their account becomes
is_active = True. - I’m using Djoser’s
/reset_passwordendpoint ( Base Endpoints — djoser 2.3.3 documentation) to allow the user to set their account password.
I can’t think of another way to implement this flow using Djoser. How problematic is it to use this route for this purpose? Below are some snippets from my view and serializers.
from djoser.serializers import UserCreateSerializer as DjoserUserCreateSerializer
from django.contrib.auth import get_user_model
from rest_framework import serializers
from users.models import User
User = get_user_model()
# Este serializador será usado apenas para a criação inicial sem senha
class UserRegistrationSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'username')
extra_kwargs = {
'username': {'required': True} # Garante que o username é obrigatório
}
def create(self, validated_data):
user = User.objects.create_user(
email=validated_data['email'],
username=validated_data['username'],
is_active=False # O usuário só será ativo após definir a senha
)
return user
# Este serializador pode ser usado para outras operações com o usuário
class UserSerializer(DjoserUserCreateSerializer):
class Meta(DjoserUserCreateSerializer.Meta):
model = User
fields = ('id', 'email', 'username', 'password', 'phone_number', 'address', 'is_active', 'is_staff')
# users/views.py
from users.serializers import UserRegistrationSerializer
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from django.contrib.auth import get_user_model
from djoser.email import PasswordResetEmail
from djoser.conf import settings as djoser_settings
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.contrib.auth.tokens import default_token_generator
User = get_user_model()
class RegisterInitialView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserRegistrationSerializer
permission_classes = [AllowAny]
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save() # Salva o usuário com is_active=False
if user: # verifica se o usuário foi criado com sucesso
uid = urlsafe_base64_encode(force_bytes(user.pk))
token = default_token_generator.make_token(user)
context = {
'user': user,
'uid': uid,
'token': token,
'site_name': 'Sua Plataforma',
'domain': '127.0.0.1:8000',
'protocol': 'http'
# site_name, domain e protocol devem ser ajustados em produção
}
try: # envia o e-mail de redefinição de senha
email = PasswordResetEmail(request=request, context=context)
email.send([user.email])
user.is_active = True
user.save()
return Response({'detail': 'User registered. Please check your email to set your password.'}, status=status.HTTP_201_CREATED)
except Exception as e:
# Caso o envio de e-mail falhe printa log do erro
print(f"Erro ao enviar e-mail: {e}")
# remove o usuário inativo criado se o e-mail falhar
user.delete()
return Response({'detail': 'User registration failed due to email sending error. Please try again later.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
from django.contrib import admin
from django.urls import path, include
from rest_framework.permissions import AllowAny
from users.views import RegisterInitialView
from djoser.views import UserViewSet
urlpatterns = [
path('admin/', admin.site.urls),
path('auth/users/set_password/', UserViewSet.as_view({'post': 'set_password'}, permission_classes=[AllowAny]), name='user-set-password'),
path('auth/users/reset_password/', UserViewSet.as_view({'post': 'reset_password'}, permission_classes=[AllowAny]), name='user-reset-password'),
# path('auth/users/activation/', UserViewSet.as_view({'post': 'activation'}, permission_classes=[AllowAny]), name='user-activation'),
path('auth/register/initial/', RegisterInitialView.as_view(), name='register_initial'),
path('auth/', include('djoser.urls')),
path('auth/', include('djoser.urls.authtoken')),
]