How do I hash passwords in DRF?

Viewed 1513

I have a problem with serialized data that Django REST Framework outputs because the password fields aren't hashed (especially the ones created direct from DRF). The user that I created using createsuperuser command has the serialized password hashed (pbkdf2) which is fine, but the ones that I create using DRF aren't. The serializer that I use looks like this (serializers.py):

from users.models import User

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(style={'input_type': 'password'})
    class Meta:
        model = User
        fields = '__all__'

The model class looks like this (models.py):

from django.contrib.auth.models import AbstractUser

# Create your models here.

class User(AbstractUser):
    class Role:
        ADMINISTRATOR = 'Administrator'
        PHARMACIST = 'Pharmacist'
        PATIENT = 'Patient'

        choices = {
            (ADMINISTRATOR, 'Administrator'),
            (PHARMACIST, 'Pharmacist'),
            (PATIENT, 'Patient'),
        }

    role = models.CharField(max_length=100, choices=Role.choices, default=Role.ADMINISTRATOR)
    first_name = models.CharField(max_length=100, blank=False)
    last_name = models.CharField(max_length=100, blank=False)
    email = models.EmailField(max_length=100, unique=True)
    birth_date =  models.DateField(auto_now_add=False, blank=True, null=True)
    address = models.CharField(max_length=255)

And the queryset looks like this (views.py):

from django.shortcuts import render
from users.models import User
from rest_framework import viewsets
from users.serializers import UserSerializer

# Create your views here.


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

I am looking forward for your help.

2 Answers

This thing worked for me:

def create(self, validated_data):
    user = super(UserSerializer, self).create(validated_data)
    user.set_password(validated_data['password'])
    user.save()
    return user

Now, the passwords are serialized.

You could override .to_internal_value(). (This has the advantage over overriding create() of only saving the object once.)

This may act strangely upon editing the user, though. YMMV :)

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(style={'input_type': 'password'})

    class Meta:
        model = User
        fields = '__all__'

    def to_internal_value(self, validated_data):
        raw_password = validated_data.pop('password')
        user = super().to_internal_value(validated_data)
        user.set_password(raw_password)  # Hash the raw password
        return user
Related