How to implement password change form in Django 1.9

21,365

Solution 1

You can see the Change Password section of following documentation for this. How to change password in Django. It works like this:

  1. Navigation to your project where manage.py file lies

  2. $ python manage.py shell

  3. Execute the following:

    from django.contrib.auth.models import User
    u = User.objects.get(username__exact='john')
    u.set_password('new password')
    u.save()
    

You will have to make a formset and you will perform this action at submission of the form.

You can also use the simple manage.py command:

manage.py changepassword *username*

Just enter the new password twice.

For second part of your question (User cannot choose old password), you can create a table in which you will store user's old password. When user will enter new password, you can check this in that table whether he can choose it or not. Django has a function check_password which is used to compare two passwords.

Solution 2

why don't you use django's built-in PasswordChangeForm (django.contrib.auth.forms).

If you like the way it works just use this from, or you can create a new one that inherits PasswordChangeForm

    class PasswordChangeCustomForm(PasswordChangeForm):
        error_css_class = 'has-error'
        error_messages = {'password_incorrect':
                  "Το παλιό συνθηματικό δεν είναι σωστό. Προσπαθείστε   ξανά."}
        old_password = CharField(required=True, label='Συνθηματικό',
                      widget=PasswordInput(attrs={
                        'class': 'form-control'}),
                      error_messages={
                        'required': 'Το συνθηματικό δε μπορεί να είναι κενό'})

        new_password1 = CharField(required=True, label='Συνθηματικό',
                      widget=PasswordInput(attrs={
                        'class': 'form-control'}),
                      error_messages={
                        'required': 'Το συνθηματικό δε μπορεί να είναι κενό'})
        new_password2 = CharField(required=True, label='Συνθηματικό (Επαναλάβατε)',
                      widget=PasswordInput(attrs={
                        'class': 'form-control'}),
                      error_messages={
                        'required': 'Το συνθηματικό δε μπορεί να είναι κενό'})

I will provide later an example of the clean and save methods

see here for more details

Solution 3

Easy peasy, if you

from django.contrib.auth.forms import PasswordChangeForm


Then

class MyChangeFormPassword(PasswordChangeForm):
    pass

On your view

def get(self, request):
    instance_user = get_object_or_404(User, id=int(user_id))
    form_edit_password = MyChangeFormPassword(instance_user)
    context={'form_edit_password': form_edit_password}

    return render(request, self.template_name, context)

On your template

<div class="col-lg-4">
   {{form_edit_password.old_password}}
</div>
<div class="col-lg-4">
   {{form_edit_password.new_password1}}
</div>
<div class="col-lg-4">
  {{form_edit_password.new_password2}}
</div>

And your post

form_edit_password = ChangePasswordForm(user, data=put)
if form_edit_password.is_valid():
    form_edit_password.save()
    return self.__succes_response(_('Password updated'))
else:
    return self.__error_response([form_edit_password])

Django will do everything for you, crazy isn't? You could write your own rules on the MyChangeFormPassword overwriting the parent methods, but this is a great aproach,

I am writing this for Django 3.

Solution 4

Since you are using you custom user model a nice way to implement the functionality is to create a new form ChangePassword:

class ChangePassword(forms.Form):
      old_password=forms.PasswordField()
      new_password=forms.PasswordField()
      reenter_password=forms.PasswordField()
      def clean(self):
          new_password=self.cleaned_data.get('new_password')
          reenter_password=self.cleaned_data.get('reenter_password')
          #similarly old_password
         if new_password and new_password!=reenter_password or new_password==old_password:
                #raise error
         #get the user object and check from old_password list if any one matches with the new password raise error(read whole answer you would know) 
         return self.cleaned_data #don't forget this.

You can define clean() to check that both passwords match or not, and also the new password entered is not same as the old password.

If you don't want the user to use a password they have used before an option would be

  1. Create a new field (if you want to store these passwords as plain strings)
  2. Create a model containing hashed previous passwords (for better security).

According to your models you are not encrypting passwords so option 1 is good for you. In case you want to encrypt you can choose sha256 for it, library is passlib; just search google.

To implement option 1 just add a field to your model and whenever password is changed, append the old password to this field contents. You can either use a CharField but its maximum length is only 255 instead you can choose textfield, for your model it would be like:

class Members(models.Model):
       #rest fields..
       old_passwords=models.TextField(blank=True,default='')

Now when saving ChangePassword use the cleaned data to update the member password:

def change_password(request):
         if request.method=='POST':
            form=ChangePassword(request.POST)
            if form.is_valid():
                  new_pass=form.cleaned_data['new_password']
                  #get the current user object as user
                  if user.old_password=='':
                         #it's first time user is changing password
                         #populate our Members old_password_field
                         user.old_password=user.password
                  else:         
                         user.old_password=user.old_password+','+user.password
                  user.password=new_password 
                  user.save()
                  #do whatever you want to do man..

The code is just to help you understand what you need to do, you have to do thing your own way!

Solution 5

IN urls.py

path('password-reset/', views.ChangePassword.as_view(), name='password-reset'),

Password change form:

class MyPasswordChangeForm(PasswordChangeForm):
    def __init__(self, user, *args, **kwargs):
        self.user = user
        super().__init__(user, *args, **kwargs)
        self.fields['old_password'].widget.attrs.update({'class': 'form-control', 'placeholder': "Old Password"})
        self.fields['new_password1'].widget.attrs.update({'class': 'form-control', 'placeholder': "New Password"})
        self.fields['new_password2'].widget.attrs.update({'class': 'form-control', 'placeholder': "New Password"})

    def save(self, commit=True):
        password = self.cleaned_data["new_password1"]
        self.user.set_password(password)
        if commit:
            self.user.save()
        return self.user

In views.py:

from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin

class ChangePassword(LoginRequiredMixin,TemplateView):

    def get(self, request, *args, **kwargs):
        form_class = MyPasswordChangeForm
        form = self.form_class(self.request.user)
        return render(request, 'password.html',{'form': form,})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.user, request.POST)
        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)  # Important!
            return render(request, 'password.html', {'form': form, 'password_changed': True})
        else:
            return render(request, 'password.html', {'form': form, 'password_changed': False})

In password.html:

    <div id='PasswordChnageForm'>
        <form method="post" action="{% url 'password-reset' %}">
        {% csrf_token %}
            {% for field in form %}
                    {{ field }}
                    {% if field.help_text %}
                        <small style="display: none">{{ field.help_text }}</small>
                    {% endif %}
                    {% for error in field.errors %}
                        <p style="color: red">{{ error }}</p>
                    {% endfor %}
    
            {% endfor %}
                <input type="submit" name="save" value="Save" >
         </form>
    </div>
    <script src="{% static 'web_admin/js/jquery-3.4.1.min.js' %}"></script>
    <script>
        $("#CallPasswordChangeButton").on('click', function (e) {
            e.preventDefault(); // avoid to execute the actual submit of the form.
            var form = $('#PasswordChnageForm');
            $.ajax({
                type: 'Post',
                url: "{% url 'password-reset' %}",
                data: form.serialize(),
                success: function (data) {
                    $('#password_change').empty().html(data);
                }
            });
        });
    </script>
Share:
21,365
Admin
Author by

Admin

Updated on October 26, 2021

Comments

  • Admin
    Admin over 2 years

    The url for my python project is here: https://github.com/abylikhsanov/social

    I am trying to implement the password change form, so the user can change his or her password. How can I implement it? The application should keep track of the previous password and should not allow the user to use a previously used password. Also, I want to implement the reset password function.

  • Admin
    Admin over 8 years
    So template_name - should be my custom template for reseting the password? The link you've provided, will it be enough to use the example template?
  • Admin
    Admin over 8 years
    Did you mean old_password=forms.PasswordInput()? As mine cant resolve yours
  • Admin
    Admin over 8 years
    Also, what does the "user" means here? Do I need to create a separate class for storing users?
  • Admin
    Admin over 8 years
    Also, the 'return self.cleaned_data ' gives me "return outside of function"
  • rdRahul
    rdRahul over 8 years
    yes, it was forms.PasswordInput(), and user object means your Member's class object , return should work fine try reading from here docs.djangoproject.com/en/1.9/ref/forms/validation
  • Greg Schmit
    Greg Schmit about 7 years
    This doesn't seem to answer the question. The OP wants to have a change password form (and view/template), along with password history.
  • Muhammad Hassan
    Muhammad Hassan about 7 years
    @GregSchmit The answer of functionality which he is asking will be very long in my opinion. That is why I did not write the whole code. Instead I wrote how can he solve his problem.
  • Greg Schmit
    Greg Schmit about 7 years
    I agree that it is a broad, poorly-put question.