DEV Community

HTTP
HTTP

Posted on

Django: Toggle Password Visibility With a Button Using Crispy Forms

In this tutorial you will learn how to create a button attached to a password input form that toggles the visibility of your password.

django version==4.2.2

example-GIF

Start by installing the following packages:

pip install django-crispy-forms
Enter fullscreen mode Exit fullscreen mode
pip install crispy-bootstrap5
Enter fullscreen mode Exit fullscreen mode

Make sure to include them both in installed apps, in settings.py:

INSTALLED_APPS = [
    # ...
    'crispy_forms',
    'crispy_bootstrap5'
]
Enter fullscreen mode Exit fullscreen mode

And define the CRISPY TEMPLATES PACK in settings.py:

CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
CRISPY_TEMPLATE_PACK = 'bootstrap5'
Enter fullscreen mode Exit fullscreen mode

Add the following CDN links to your template:

<!-- Bootstrap CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!-- Bootstrap icons CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">

Enter fullscreen mode Exit fullscreen mode

In forms.py (in the same directory as views.py), add the following form class:

from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div
from crispy_forms.bootstrap import FieldWithButtons, StrictButton

class CreateUserForm(UserCreationForm):
    helper = FormHelper()
    helper.form_tag = False
    helper.layout = Layout(
        Div(
            Div('username', css_class='col-12'),
            Div(FieldWithButtons('password1', StrictButton('<i class="bi bi-eye"></i>', type='button', css_class='btn btn-outline-secondary', id='password1Button')), css_class='col-6'
                ),
            Div(FieldWithButtons('password2', StrictButton('<i class="bi bi-eye"></i>', type='button', css_class='btn btn-outline-secondary', id='password2Button')), css_class='col-6'
                ),
            css_class='row'
        )
    )

    class Meta:
        model = get_user_model()
        fields = ['username', 'password1', 'password2']
Enter fullscreen mode Exit fullscreen mode

In views.py, create the SignupView, if you use Class Based Views:

from django.urls import reverse_lazy
from django.views.generic import CreateView
from .forms import CreateUserForm

class SignUpView(CreateView):
    template_name = 'signup.html'
    form_class = CreateUserForm
    success_url = reverse_lazy('')
Enter fullscreen mode Exit fullscreen mode

If you use Function Based Views, add the following code to views.py:

from .forms import CreateUserForm
from django.shortcuts import render

def sign_up(request):
    if request.method == 'POST':
        form = CreateUserForm(request.POST)
        if form.is_valid():
            form.save()
    else:
        form = CreateUserForm()
    return render(request, 'signup.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode

Add the following html code to signup.html:

{% load crispy_forms_tags %}

<script>
  document.addEventListener('DOMContentLoaded', function () {
    for (i of [1, 2]) {
      const togglePassword = document.querySelector(`#password${i}Button`);
      const password = document.querySelector(`#id_password${i}`);

      togglePassword.addEventListener('click', function (e) {

        const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
        password.setAttribute('type', type);

        if (this.firstChild.classList.contains('bi-eye-slash')) {
          this.firstChild.classList.remove('bi-eye-slash')
          this.firstChild.classList.add('bi-eye');
        }
        else {
          this.firstChild.classList.remove('bi-eye');
          this.firstChild.classList.add('bi-eye-slash');
        }
      });
    }
  });
</script>

<form method="post">
    {% csrf_token %}
    {% crispy form %}

</form>
Enter fullscreen mode Exit fullscreen mode

Make sure you do include

{% crispy form %}

instead of the usual

{{ form|crispy }}

This ensures that the modifications to the form made in forms.py actually see the daylight.

Finally, add the URL of the view to urls.py:

from django.urls import path
from . import views

urlpatterns = [
    # Class Based Views
    path('signup/', views.SignUpView.as_view(), name='signup'),
    # Function Based Views (Choose one of the two)
    path('signup/', views.sign_up, name='signup')
]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)