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.

Benjamin Smith Max
  • 2,668
  • 8
  • 33
  • 56

4 Answers4

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.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • 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. – Fed Sep 17 '21 at 14:50
  • 1
    @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. – Alasdair Sep 18 '21 at 21:46
5

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)
dakaii
  • 61
  • 1
  • 2
  • 1
    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. – melwil Apr 14 '20 at 10:17
  • This actually solved the issue I had using the Django rest framework, I forgot to do this. Cheers – Onengiye Richard Jun 10 '21 at 18:43
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']
stocke777
  • 111
  • 1
  • 2
0

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