HOWTO · Django

Django Changer le mot de passe

Nous apprendrons avec cette explication comment nous créons une page Web pour que l'utilisateur change le mot de passe après s'être connecté sur la page Web dans Django.

Sur cette page

La vue intégrée de Django PasswordChangeView permet à un utilisateur connecté de modifier le mot de passe de son compte actuel. Cette opération diffère d’une procédure de réinitialisation du mot de passe, qui permet à un utilisateur de retrouver l’accès à son compte sans connaître son mot de passe actuel. L’implémentation ci-dessous relie le formulaire, la page de confirmation, les noms d’URL et un lien depuis la page d’accueil.

Configurer les vues de changement de mot de passe

Dans users/views.py, créez une sous-classe de PasswordChangeView et indiquez-lui le modèle du formulaire. Définissez success_url avec reverse_lazy afin que Django résolve la destination nommée une fois la configuration des URL chargée. Pour la page de confirmation, utilisez PasswordChangeDoneView ; PasswordResetDoneView est destiné aux procédures de réinitialisation du mot de passe.

Cet exemple autonome de configuration utilise de petits substituts de Django afin de présenter les noms et leur raccordement sans projet Django. Dans votre projet, conservez les imports et les définitions de l’application, mais omettez la configuration du banc d’essai et les assertions finales. Le formulaire Django de changement de mot de passe vérifie le mot de passe actuel soumis et la confirmation du nouveau mot de passe au moyen des validateurs configurés.

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

# Harness only: stand-ins let this configuration run without Django installed.
import sys
import types

django = types.ModuleType("django")
contrib = types.ModuleType("django.contrib")
auth = types.ModuleType("django.contrib.auth")
auth_views = types.ModuleType("django.contrib.auth.views")
urls = types.ModuleType("django.urls")
django.__path__ = []
contrib.__path__ = []
auth.__path__ = []

class View:
    @classmethod
    def as_view(cls):
        return cls

class PasswordChangeView(View):
    pass

class PasswordChangeDoneView(View):
    pass

def reverse_lazy(name):
    return name

def path(route, view, name=None):
    return (route, view, name)

def include(module, namespace=None):
    return (module, namespace)

auth_views.PasswordChangeView = PasswordChangeView
auth_views.PasswordChangeDoneView = PasswordChangeDoneView
urls.reverse_lazy = reverse_lazy
urls.path = path
urls.include = include
sys.modules.update({
    "django": django,
    "django.contrib": contrib,
    "django.contrib.auth": auth,
    "django.contrib.auth.views": auth_views,
    "django.urls": urls,
})

users_views = types.ModuleType("users_views")
users_views.ChangePasswordView = None
users_views.ChangePasswordDoneView = None
sys.modules["users_views"] = users_views

# users/views.py
from django.contrib.auth.views import PasswordChangeDoneView, PasswordChangeView
from django.urls import reverse_lazy

class ChangePasswordView(PasswordChangeView):
    template_name = "users/password-change.html"
    success_url = reverse_lazy("users:password-change-done")

class ChangePasswordDoneView(PasswordChangeDoneView):
    template_name = "users/password-change-done.html"

users_views.ChangePasswordView = ChangePasswordView
users_views.ChangePasswordDoneView = ChangePasswordDoneView

# users/urls.py
# In the project file, import these classes from .views.
from django.urls import path
from users_views import ChangePasswordDoneView, ChangePasswordView

app_name = "users"
urlpatterns = [
    path("change-password/", ChangePasswordView.as_view(), name="password-change"),
    path("change-password/done/", ChangePasswordDoneView.as_view(), name="password-change-done"),
]

# project urls.py
from django.urls import include, path

project_urlpatterns = [
    path("users/", include("users.urls", namespace="users")),
]

assert ChangePasswordView.success_url == "users:password-change-done"
assert [route[2] for route in urlpatterns] == ["password-change", "password-change-done"]
assert project_urlpatterns[0][1] == ("users.urls", "users")

Dans votre projet, le fichier d’URL de l’application importe les vues avec from .views import ChangePasswordDoneView, ChangePasswordView ; l’exemple utilise users_views pour regrouper le tout dans un seul bloc exécutable de façon autonome. Dans le fichier d’URL du projet, importez include et path depuis django.urls, puis utilisez la même configuration path("users/", include("users.urls", namespace="users")) que celle présentée dans l’exemple.

Enregistrer l’application et les modèles d’URL du projet

L’exemple ci-dessus inclut les deux niveaux d’URL. Dans le projet, users/urls.py définit app_name = "users", et la configuration des URL du projet inclut ce module avec namespace="users". Ensemble, ces définitions permettent de résoudre users:password-change-done ; les noms des routes de l’application et la cible reverse_lazy doivent correspondre.

Créer les modèles de changement de mot de passe et de confirmation

Créez users/templates/users/password-change.html et faites-le hériter de votre modèle partagé base.html. Ce modèle envoie le formulaire à la même URL, inclut {% csrf_token %} dans le formulaire et affiche {{ form.as_p }} pour les champs du mot de passe actuel et du nouveau mot de passe. Le jeton CSRF contribue à la protection de Django contre les requêtes falsifiées ; il ne remplace ni l’authentification ni la validation du mot de passe.

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

{% extends 'base.html' %}

{% block title %}Change password{% endblock title %}

{% block content %}
<h1>Change your password</h1>
<form action="" method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Change password</button>
</form>
{% endblock content %}

Créez users/templates/users/password-change-done.html pour afficher la confirmation de réussite. Faites-le hériter du même modèle de base, confirmez que le mot de passe a été modifié et ajoutez un lien vers la route home-view du projet. Une requête POST réussie redirige vers cette page ; l’afficher à elle seule ne modifie pas le mot de passe.

{% extends 'base.html' %}

{% block title %}Password changed{% endblock title %}

{% block content %}
<h1>Your password has been changed</h1>
<a href="{% url 'home-view' %}">Home page</a>
{% endblock content %}

Ajouter un lien depuis la page d’accueil

Dans le modèle de la page d’accueil, ajoutez un lien vers le formulaire avec {% url 'users:password-change' %}. La vue intégrée de Django pour le changement de mot de passe exige un utilisateur connecté : placez donc le lien dans la navigation des utilisateurs connectés ou laissez la vue rediriger les visiteurs qui ne le sont pas.

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

<a href="{% url 'users:password-change' %}">Change password</a>

Exécuter et vérifier le parcours de changement de mot de passe

Démarrez le projet selon sa configuration de développement habituelle, connectez-vous avec un compte de test, puis suivez le nouveau lien de la page d’accueil. Saisissez le mot de passe actuel ainsi qu’un nouveau mot de passe valide et confirmé à l’identique. Vérifiez que Django redirige vers la page de confirmation, puis déconnectez-vous et reconnectez-vous avec le nouveau mot de passe. Il s’agit d’une procédure de vérification manuelle, et non d’un compte rendu d’exécution dans l’environnement de cet article.

Voici une vérification concise des noms d’URL qui illustre le lien entre l’espace de noms et la route :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

app_name = "users"
route_name = "password-change-done"
success_target = f"{app_name}:{route_name}"
print(success_target)

Résoudre les erreurs de configuration courantes

Si Django indique qu’il ne peut pas inverser users:password-change-done, vérifiez que app_name, l’espace de noms défini dans l’inclusion du projet, le name de la route et la cible reverse_lazy correspondent. Une erreur TemplateDoesNotExist indique généralement que le chemin du modèle dans la vue ne correspond pas à son emplacement sous le répertoire templates/users/ de l’application. Vérifiez l’orthographe et assurez-vous que l’application est installée afin que Django puisse détecter ses modèles.

Si le formulaire renvoie une erreur CSRF, conservez {% csrf_token %} dans le formulaire POST et vérifiez que la page est soumise par le flux de requêtes habituel de Django. Si un visiteur est redirigé vers la page de connexion, ce comportement est attendu pour cette fonctionnalité réservée aux utilisateurs connectés ; connectez-vous avant de tester le changement de mot de passe. Si la page de confirmation s’affiche sans que le mot de passe ait été modifié, vérifiez que le formulaire est envoyé à la vue et que le mot de passe actuel saisi ainsi que la confirmation du nouveau mot de passe satisfont aux validateurs du projet.