django custom user model password is not being hashed
Asked Answered
P

4

24

I have my own custom User model, and its own Manger too.

models:

class MyUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    first_name = models.CharField(max_length=35)
    last_name = models.CharField(max_length=35)
    username = models.CharField(max_length=70, unique=True)
    date_of_birth = models.DateField()
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    @property
    def is_staff(self):
        return self.is_admin

    def get_full_name(self):
        return ('%s %s') % (self.first_name, self.last_name)

    def get_short_name(self):
        return self.username

    objects = MyUserManager()
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name', 'username', 'date_of_birth']

manager:

class MyUserManager(BaseUserManager):
    def create_user(self, email, first_name, last_name, username, date_of_birth, password=None, **kwargs):
        if not email:
            raise ValueError('User must have an email address')

        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            username=username,
            date_of_birth=date_of_birth,
            **kwargs
        )
        user.set_password(self.cleaned_data["password"])
        user.save(using=self._db)
        return user

    def create_superuser(self, email, first_name, last_name, username, date_of_birth, password, **kwargs):
        user = self.create_user(
            email,
            first_name=first_name,
            last_name=last_name,
            username=username,
            date_of_birth=date_of_birth,
            password=password,
            is_superuser=True,
            **kwargs
        )
        user.is_admin = True
        user.save(using=self._db)
        return user

Everything works when creating a new user without any errors. But when I try to login I can't. So I checked the user's password to confirm and the password is displayed as plain text strongpassword, and when changed admin form to get the hashed password using ReadOnlyPasswordHashField I get an error inside the password field, even though I used set_password() for the Manger inside the create_user() function.

Invalid password format or unknown hashing algorithm

However, if I manually do set_password('strongpassword') for that user it is then hashed. Could you please help me solve this problem. Thank you.

Passional answered 12/11, 2015 at 14:56 Comment(3)
My guess is, your self.create_superuser is not calling your custom self.create_user, rather calling the default. Can you put a breakpoint and inspect ?Hovercraft
@Hovercraft I have no idea how to put breakpoint sorry.Passional
Try wrapping your password with this: from django.contrib.auth.hashers import make_password. So just use make_password(password).Gabby
K
20

It looks like you created a user in a way that does not use your manager's create_user method, for example through the Django admin.

If you create a custom user, you need to define a custom model form and model admin that handles the password properly.

Otherwise, passwords will not hashed when a user is created through the Django admin.

The example in docs for creating a custom users shows how to create the model form and model admin.

Knesset answered 12/11, 2015 at 15:6 Comment(2)
it would be helpful to provide a working example in the answer instead of pointing to third-party docs. I am trying for example to use the 'full example' in the docs but as there are many moving parts, it is not clear why it is not working and which bit is relevant to actually solve the issue. Thanks.Chaparajos
@FedericoCapaldo I disagree. You might be able to find a blog post with a full example if you search for it, but I don’t think this answer is the best place for one. I prefer to link to the official docs. If I see a potential improvement I’d rather submit a patch to change the docs, rather than duplicating them here.Knesset
I
6

I know it's too late now, but I'll just post this for future reference. If you're creating a new user by calling the save function on its serializer, you'll need to override the create function of the serializer as shown below, (which is pretty obvious, but I got stuck on it for a little bit....)

class SignUpView(views.APIView):
    authentication_classes = ()
    permission_classes = (permissions.AllowAny,)

    def post(self, request, format=None):
        serializer = UserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
class UserSerializer(serializers.ModelSerializer):

    password = serializers.CharField(
        min_length=6, write_only=True, required=True)

    class Meta:
        model = User
        fields = (
            'id', 'email', 'password', 'is_staff',
            'is_active', 'date_joined')

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)
Individuation answered 14/4, 2020 at 9:6 Comment(2)
This looks like Django Rest Framework code to me? It doesn't really answer the question that was asked here, even though it may have solved a related issue for you in DRF.Journeyman
This actually solved the issue I had using the Django rest framework, I forgot to do this. CheersAviator
I
2

Late answer but anyway, you need to make Custom User Model form too with explicit hashing. Else just make form inheriting UserCreationForm like:

from .models import MyUser
from django.contrib.auth.forms import UserCreationForm    
class UserForm(UserCreationForm):

    class Meta:
        model = User
        fields = ['email']
Indefatigable answered 28/1, 2021 at 16:10 Comment(0)
I
1

Add this in your UserSerialzer. Basically you have to override the create method in order to hash the password.

def create(self,validated_data):
        user = User.objects.create(email = validated_data['email'])
        user.set_password(validated_data['password'])
        user.save()
        return user
Interesting answered 23/2, 2023 at 19:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.