Gnanavel
Gnanavel

Reputation: 665

password field is missing in forms django?

I want to replace username with email in django authentication.So when I was going through documentation it says that

If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you

So First I created a custom user model which extends AbstractUser.In which I made username=None and USERNAME_FIELD='email'

class User(AbstractUser):
    username = None
    first_name = None
    last_name = None
    email = models.EmailField(unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

Then I created a modelform which uses custom user model.When I render this form in my template, password and confirm password field is missing.I don't know whats going wrong.

class UserForm(forms.ModelForm):
    class Meta:
        model = get_user_model()
        fields = ['email']

I thought password and confirm password will be there by default.Am I right?

Upvotes: 0

Views: 1797

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477533

I thought password and confirm password will be there by default.Am I right?

No. A ModelForm has logic to construct a Form based on the model you provide. But it does not treat a user model differently than another model. If you specify fields = ['email'], it will simply create a form with the email field, as the only one.

To make matters even worse, it will not create a correct user object, since the passwords should be hashed, you can store the hashed password with the .set_password(…) method [Django-doc]. We thus can create a form that looks like:

class UserCreateForm(forms.ModelForm):
    password = forms.CharField(
        label='Password',
        strip=False,
        widget=forms.PasswordInput()
    )

    def save(self, *args, **kwargs):
        self.instance.set_password(self.cleaned_data['password'])
        return super().save(*args, **kwargs)
    
    class Meta:
        model = get_user_model()
        fields = ['email']

If you want to validate the passwords, you need to add some extra logic:

from django.core.exceptions import ValidationError

class UserCreateForm(forms.ModelForm):
    password = forms.CharField(
        label='Password',
        strip=False,
        widget=forms.PasswordInput()
    )
    password2 = forms.CharField(
        label='Repeat password',
        strip=False,
        widget=forms.PasswordInput()
    )

    def clean(self, *args, **kwargs):
        cleaned_data = super().clean(*args, **kwargs)
        if cleaned_data['password'] != cleaned_data['password2']:
            raise ValidationError('Passwords do not match')
        return cleaned_data

    def save(self, *args, **kwargs):
        self.instance.set_password(self.cleaned_data['password'])
        return super().save(*args, **kwargs)
    
    class Meta:
        model = get_user_model()
        fields = ['email']

Upvotes: 0

GProst
GProst

Reputation: 10237

You need to specify all the fields you want to be in the form:

class UserForm(forms.ModelForm):
  password = forms.CharField(widget=forms.PasswordInput())
  password_confirm = forms.CharField(widget=forms.PasswordInput())

  class Meta:
    fields = ['email', 'password']

  def clean(self):
    cleaned_data = super().clean()
    password = cleaned_data.get("password")
    password_confirm = cleaned_data.get("password_confirm")

    if password != password_confirm:
        self.add_error('password_confirm', "Password does not match")

    return cleaned_data

Note however that you need to manually validate that password_confirm field matches password field

Upvotes: 1

Related Questions