Aaron Presley

Add Confirm Password Field to UserSerializer in Django Rest Framework December 9, 2015

I'm pretty new to the Django Rest Framework in general, but I had a hell of a time figuring out how to add a "confirm_password" field to my UserSerializer model.

The currently suggested methods seem overcomplicated for what I was wanting to do - which is just confirm and validate the user's password!

Eventually I realized I could just make a new Serializer that only handled my registration fields. I'm not sure if this is the best way to do it, but it seems more straightforward than subclassing a field and making my own.

Here's my eventual solution - I just created a UserRegistration Class that's not tied to a model. This allowed me to add whatever fields I wanted without worrying about adding a non-model field to the UserSerializer.

from rest_framework import serializers, viewsets
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework.decorators import (api_view, permission_classes, list_route)

from django.http import Http404, HttpResponseForbidden
from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'email', 'first_name', 'last_name',)
        write_only_fields = ('username',)

    def validate(self, data):
        # Making sure the username always matches the email
        email = data.get('email', None)
        if email:
            data['username'] = email

        return data

class UserRegistrationSerializer(serializers.Serializer):
    email = serializers.EmailField()
    first_name = serializers.CharField(required=False)
    last_name = serializers.CharField(required=False)
    password = serializers.CharField()
    confirm_password = serializers.CharField()

    def validate_email(self, email):
        existing = User.objects.filter(email=email).first()
        if existing:
            raise serializers.ValidationError("Someone with that email "
                "address has already registered. Was it you?")

        return email

    def validate(self, data):
        if not data.get('password') or not data.get('confirm_password'):
            raise serializers.ValidationError("Please enter a password and "
                "confirm it.")

        if data.get('password') != data.get('confirm_password'):
            raise serializers.ValidationError("Those passwords don't match.")

        return data

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @list_route(methods=['post'], permission_classes=[AllowAny])
    def register(self, request):
        # Validating our serializer from the UserRegistrationSerializer
        serializer = UserRegistrationSerializer(data=request.data)

        # Everything's valid, so send it to the UserSerializer
        model_serializer = UserSerializer(data=serializer.data)

        return Response(model_serializer.data)

    def info(self, request):
        serializer = UserSerializer(request.user)
        return Response(serializer.data)

    def list(self, request):
        user = request.user
        if not user or not user.is_superuser:
            return HttpResponseForbidden()
        return super(UserViewSet, self).list(request)

    def update(self, request, pk=None):
        user = User.objects.filter(id=pk).first()
        if not user or request.user != user:
            return HttpResponseForbidden()
        return super(UserViewSet, self).update(request)

As I said: I'm pretty new to DRF, but hopefully this helps anyone Googling to their wit's end like I was.