1
votes

I'm trying to update a custom user model using Django Rest Framework (via a PUT call), and while I can get my model's username and password to update properly, the password will not do so: when I check the user's password via django admin I get "Invalid password format or unknown hashing algorithm."

Here are the relevant portions of my views.py and serializers.py:

class AccountViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = Account.objects.all()
serializer_class = AccountSerializer

def get_permissions(self):
    if self.request.method in permissions.SAFE_METHODS:
        return (permissions.IsAdminUser(),)

    if self.request.method == 'POST':
        return (permissions.AllowAny(),)

    return (permissions.IsAdminUser(),)

def create(self, request):
    serializer = self.serializer_class(data=request.data)

    if serializer.is_valid(raise_exception=True):
        Account.objects.create_user(**serializer.validated_data)

        return Response(serializer.validated_data, status = status.HTTP_201_CREATED)

    return Response({
        'status': 'Bad request',
        'message': 'Account could not be created with received data.'
    }, status = status.HTTP_400_BAD_REQUEST)

_

class AccountSerializer(serializers.HyperlinkedModelSerializer):

password = serializers.CharField(write_only = True, required = False)
confirm_password = serializers.CharField(write_only = True, required = False)

def validate(self, data):
    if data.get('password') != data.get('confirm_password'):
        raise serializers.ValidationError("Passwords do not match.")
    else:
        temp = Account(email = data.get('email'), username = data.get('username'))
        validate_password(data.get('password'), temp)
    return data

class Meta:

    model = Account

    fields = (
        'id',
        'email',
        'username',
        'created_at',
        'updated_at',
        'password',
        'confirm_password'
    )
    read_only_fields = (
        'created_at',
        'updated_at'
    )

    def create(self, validated_data):
        return Account.objects.create(**validated_data)

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)

        instance.save()

        update_session_auth_hash(self.context.get('request'), instance)

        return instance

Now, it is my understanding that I should not have to override the AccountViewSet's update() (provided by superclass ModelViewSet), as it ought to automatically call the serializer's save(), which in turn ought to call the serializer's update(). Being that it wasn't working, I also tried implementing my own update() in AccountViewSet, in the same style as the corresponding create() method was overridden, but that didn't seem to work either. In both cases, in fact, I couldn't even verify that the serializer's update() was being called, which makes me think my serializer's update() method is not being called and instead its superclass' update() is being called, which would explain the password not being hashed. Clearly I am doing something wrong as far as getting my own serializer's update() method called, but I can't figure out what and can't find any other answers that really solve this issue specifically.

1

1 Answers

1
votes

I don't know the problem of your code but try this serializer from https://thinkster.io/django-angularjs-tutorial

class AccountSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)
    confirm_password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = Account
        fields = ('id', 'email', 'username', 'created_at', 'updated_at',
                  'first_name', 'last_name', 'tagline', 'password',
                  'confirm_password',)
        read_only_fields = ('created_at', 'updated_at',)

        def create(self, validated_data):
            return Account.objects.create(**validated_data)

        def update(self, instance, validated_data):
            instance.username = validated_data.get('username', instance.username)
            instance.tagline = validated_data.get('tagline', instance.tagline)

            instance.save()

            password = validated_data.get('password', None)
            confirm_password = validated_data.get('confirm_password', None)

            if password and confirm_password and password == confirm_password:
                instance.set_password(password)
                instance.save()

            update_session_auth_hash(self.context.get('request'), instance)

            return instance