1

I'm trying to use Django Rest Framework 3.1.1 to create a user in a POST. I need to use the built-in method to create the encrypted password so I've tried to override the save method on the ModelSerializer but I clearly don't know Django / DRF well enough to do this. Is there a straightforward way to accomplish this?

When I try the code below, I get an error:

unbound method set_password() must be called with User instance as first argument (got unicode instance

instead)

from django.contrib.auth.models import User

from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User

    def save(self):
        email = self.validated_data['email']
        username = self.validated_data['username']
        password = User.set_password(self.validated_data['password'])
Alain O'Dea
  • 21,033
  • 1
  • 58
  • 84
user2352879
  • 159
  • 9

2 Answers2

4

Try doing something like this instead:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('email', 'username', 'password')
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User(
            email=validated_data['email']
            username=validated_data['username'],
        )
        user.set_password(validated_data['password'])
        user.save()
        return user
jape
  • 2,861
  • 2
  • 26
  • 58
  • It's better override the `update` method too, like says in this answer http://stackoverflow.com/questions/27586095/why-isnt-my-django-user-models-password-hashed/27586289. If override just the `create` method, is possible that some tools with `curl` or some external application get `500 error` in the `POST` operation on API from client. – bgarcial Jan 20 '17 at 02:15
  • @bgarcial, I don't follow what you're trying to say. It doesn't matter what client the request comes from..if it's a POST, this should work. – Gezim May 01 '17 at 00:13
3

Since you are using a ModelSerializer, you can override the perform_create() function in your view and then set the password for the user.

DRF has provided this hook to add some custom actions which should occur before or after saving an object.

As per DRF3 documentation:

These override points are particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update.

from django.contrib.auth.hashers import make_password

class MyView(..):

    ...

    def perform_create(self, serializer):
        hashed_password = make_password(serializer.validated_data['password']) # get the hashed password
        serializer.validated_data['password'] = hashed_password 
        user = super(MyView, self).perform_create(serializer) # create a user

Since, Django stores the password in hashed format and not as raw passwords, we use Django's make_password() to get the raw password in hashed format. We then set the password in validated_data of the serializer to this hashed password. This hashed password is then used by the serializer when creating the user by calling the super().

Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126