Django: Toggle Password Visibility With a Button Using Crispy Forms
Tags: django, forms, tutorial
Published: 2023-07-02 18:20:10

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rkyjkccvke4efdod9qao.gif)

Start by installing the following packages:

```
pip install django-crispy-forms
```
```
pip install crispy-bootstrap5
```

Make sure to include them both in installed apps, in _settings.py_:
```python
INSTALLED_APPS = [
    # ...
    'crispy_forms',
    'crispy_bootstrap5'
]
```

And define the CRISPY TEMPLATES PACK in _settings.py_:
```python
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
CRISPY_TEMPLATE_PACK = 'bootstrap5'
```

Add the following CDN links to your template:
```html
<!-- 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">

```

In forms.py (in the same directory as _views.py_), add the following form class:
```python
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']
```

In _views.py_, create the SignupView, if you use Class Based Views:
```python
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('')
```

If you use Function Based Views, add the following code to _views.py_:
```python
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})
```


Add the following html code to _signup.html_:
```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>
```

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_: 
```python
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')
]
```