How to implement change password modal form?

Viewed 645

EDIT 14/07/2021

Reading this blog post (https://simpleisbetterthancomplex.com/tips/2016/08/04/django-tip-9-password-change-form.html) I understand how to prevent user logout (update_session_auth_hash(self.request, self.object) added to form_valid function) and I am very close to the solution using attempt #2

BUT

there still have "grey" screen after user pasword successfully changed and user have to click on screen to make it disappeared...


EDIT 14/07/2021

I quite closeto the solution following this tutorial https://www.abidibo.net/blog/2015/11/18/modal-django-forms-bootstrap-4/

Neverthless it is not working correctly.

attempt #1: get_success_url

I override get_success_url funtion of PasswordChangeView to redirectto index page and use SuccessMessageMixin to confirm password change. Password is correctly changed but somthig is going wrong with return (see capture below)

enter image description here

attemp #2: form_valid

I've tried another way overriding form_valid function that return JsonObject. Password is also changed but screen stay as modal was still opened. When I click on screen, "grey" diappeared but if I refresh page (F5) i am redirected to home page and I am disconnected without error... and message "Your password has been successfully changed" is displayed even if modal return error...

enter image description here


I have implemented authentification using Django django.contrib.auth and it work but I would like change_password to be displayed using modal form and Ajax.

And I do not manage to even display the form inside modal (with all validation stuff).

I have already use bootstrap modal to display information but not for form submission. As it did not call a change_password view that render change_password_form.html template, form is not available and I got an error Parameter "form" should contain a valid Django Form.

How should I do this?

urls.py

class PasswordChangeView(SuccessMessageMixin, auth_views.PasswordChangeView): 
    <!-- attempt #1 -->    
    success_message = "Your password have been changed successfully."
    def get_success_url(self):
        return reverse('export:index')
    
    <!-- attempt #2 -->
    # def form_valid(self, form):
    #     self.object = form.save()
    #     update_session_auth_hash(self.request, self.object) # prevent user’s auth session to be invalidated and user have to log in again   
    #     return JsonResponse ({'data': 'success'},status = 200)

app_name = 'registration'
urlpatterns = [
    ...
    path('change_password/', PasswordChangeView.as_view(), name='password_change'),
    ...
]

password_change_form.html (modified)

{% load bootstrap4 %}
<div id = "password_change" class="modal-dialog modal-lg" role="document">
    <form action="{% url 'registration:password_change' %}" method="post" id="password_change" class="form">{% csrf_token %}
        <div class="modal-content">
            <div class='card' style="border-top-width: 0px;border-left-width: 0px;border-bottom-width: 0px;border-right-width: 0px;">
                <div style="background-color:#326690;padding:5px 5px 5px 16px;color:white;font-weight:bold;border-radius: 2px 2px 0 0;">Change password</div>
                <form method="post" class="form-signin">
                    <div class='card-body' style="padding-bottom:0px">
                        {% csrf_token %}
                        {% bootstrap_form form layout="horizontal" placeholder="None" size="medium" label_class="form-label col-md-3" %}
                    </div>
                    <hr style="margin:1px">
                    <div class='card-body' style="padding-top:5px;padding-bottom:5px;">
                        <div>           
                            <button type="submit" class="btn block" style="float:right;background-color:#326690;color:white;min-width:110px;">{% trans 'Confirm' %}</button>
                            <!--<a href="{% url 'export:index' %}" class="btn btn-light border" style="float:right;color:#326690;min-width:110px;margin-right:5px;"><i class="fa fa-times" aria-hidden="true"></i> {% trans 'Cancel' %}</a> -->                 
                            <span data-dismiss="modal" class="btn btn-light border" style="float:right;color:#326690;min-width:110px;margin-right:5px;"><i class="fa fa-times" aria-hidden="true"></i> {% trans 'Cancel' %}</span>              
                        </div>
                    </div>
                </form>
            </div>
    </form>
</div>

<script>
    var form_options = { target: '#modal', success: function(response) {
        console.log('response',response);
        //obj = JSON.parse(response);
        $("#password_change_confirm").append('<div class="alert alert-success"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button><span>Your password has been changed successfully.</span></div>');
    } };
    $('#password_change').ajaxForm(form_options);

</script>

base.html (modified)

<a data-toggle="modal" data-target="#modal" class="dropdown-item" href="{% url 'registration:password_change' %}"><i class="fa fa-key" aria-hidden="true"></i>
        {% trans 'Change password' %}</a>

<div class="modal" id="modal"></div>
        

index.html (added)

{% extends 'layouts/base.html' %}
...
    <!-- message for change password in authentification module -->
    <div id="password_change_confirm" style="padding-top:10px;padding-left:10px;padding-right:10px;"></div>

    {% for message in messages %}
    <div class="container-fluid" style="padding:10px 10px 10px 10px;">
      <div id = 'msg' class="alert {{ message.tags }} alert-dismissible" role="alert" >
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
        {{ message }}
      </div>
    </div>
    {% endfor %} 
    <!-- end message for change password in authentification module -->
1 Answers

OK, I finally find a 'hack' solution that works, do know if it is the better way but it's works:

base.html

<!-- https://www.abidibo.net/blog/2015/11/18/modal-django-forms-bootstrap-4/ -->
<div class="modal" id="modal" style="margin-top:150px;"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.3.0/jquery.form.min.js" integrity="sha384-qlmct0AOBiA2VPZkMY3+2WqkHtIQ9lSdAsAn5RUJD/3vA5MKDgSGcdmIv4ycVxyn" crossorigin="anonymous"></script>
<script type="text/javascript">
$(document).ready(function () {
    $('#modal').on('show.bs.modal', function (event) {
          var modal = $(this)
          $.ajax({
              url: "{% url 'registration:password_change' %}",
              context: document.body
          }).done(function(response) {
              modal.html(response);
          });
      })
)};

registration/views.py

class PasswordChangeView(auth_views.PasswordChangeView): 

    def form_valid(self, form):
        self.object = form.save()
        update_session_auth_hash(self.request, self.object) # prevent user’s auth session to be invalidated and user have to log in again 
        return JsonResponse ({'data': form.is_valid()},status = 200)

registration/urls.py

app_name = 'registration'
urlpatterns = [path('change_password/', PasswordChangeView.as_view(), name='password_change'),]

registration/template/registration/password_change_form.html

<!-- https://www.abidibo.net/blog/2015/11/18/modal-django-forms-bootstrap-4/ -->
{% load i18n widget_tweaks %}
{% load bootstrap4 %}
<div id = "password_change" class="modal-dialog modal-lg" role="document">
    <form action="{% url 'registration:password_change' %}" method="post" id="password_change" class="form">{% csrf_token %}
        <div class="modal-content">
            <div class='card' style="border-top-width: 0px;border-left-width: 0px;border-bottom-width: 0px;border-right-width: 0px;">
                <div style="background-color:#326690;padding:5px 5px 5px 16px;color:white;font-weight:bold;border-radius: 2px 2px 0 0;">Change password</div>
                <form method="post" class="form-signin">
                    <div class='card-body' style="padding-bottom:0px">
                        {% csrf_token %}
                        {% bootstrap_form form layout="horizontal" placeholder="None" size="medium" label_class="form-label col-md-3" %}
                    </div>
                    <hr style="margin:1px">
                    <div class='card-body' style="padding-top:5px;padding-bottom:5px;">
                        <div>           
                            <button type="submit" class="btn block" style="float:right;background-color:#326690;color:white;min-width:110px;">{% trans 'Confirm' %}</button>
                            <span data-dismiss="modal" class="btn btn-light border" style="float:right;color:#326690;min-width:110px;margin-right:5px;"><i class="fa fa-times" aria-hidden="true"></i> {% trans 'Cancel' %}</span>              
                        </div>
                    </div>
                </form>
            </div>
    </form>
</div>

<script>
    var form_options = { target: '#modal', success: function(response) {
        <!-- test for form validation status: password changed confirmation message displayed only if form is valid -->
        if(response.data == true){
            <!-- remove grey background -->
            $('body').removeClass('modal-open');
            $('.modal-backdrop').remove();
            $("#password_change_confirm").append('<div class="alert alert-success"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button><span>Your password has been changed successfully.</span></div>');
        }
            
    } };
    $('#password_change').ajaxForm(form_options);

</script>

myapp/emplate/myapp/index.html

<!-- message for change password in authentification module -->
<div id="password_change_confirm" style="padding-top:10px;padding-left:10px;padding-right:10px;"></div>
Related