web-dev-qa-db-fra.com

Django 2 - Comment enregistrer un utilisateur en utilisant la confirmation par e-mail et les CBV?

Cette question vise spécifiquement une réponse Django 2.0 car le module registration n'est pas (encore) disponible pour cela.

De plus, cela peut sembler trop large, mais je me suis souvent retrouvé dans des situations où je ne peux pas utiliser de module tiers parce que ... eh bien .. politiques. J'en suis sûr que beaucoup l'ont fait. Et je sais que regarder et rassembler des informations tirées d'ici ou Django docs était un casse-tête.


Flux de travail:

Supposons que nous ayons besoin du flux suivant:

  1. L'utilisateur accède à la page d'inscription et remplit les champs suivants: first_name, last_name et email (l'e-mail sera utilisé comme nom d'utilisateur).
  2. L'utilisateur soumet le formulaire et reçoit un e-mail de confirmation avec une URL contenant un jeton unique.
  3. Lorsque l'utilisateur clique sur le lien reçu, il est redirigé vers une page où il définira son mot de passe. Une fois terminé, il est connecté à la page du tableau de bord.

Extra-info: L'utilisateur se connectera plus tard en utilisant son email (qui est en fait son nom d'utilisateur) et son mot de passe.


Question spécifique:

  • À quoi ressembleront les modèles/vues (à l'aide de CBV)/formulaires/urls?
12
Cajuu'

Le modèle utilisateur

Tout d'abord, vous devrez créer un modèle User personnalisé et un UserManager personnalisé pour supprimer le champ username et utiliser à la place email.

Dans models.py, Le UserManager devrait ressembler à ceci:

from Django.contrib.auth.models import BaseUserManager


class MyUserManager(BaseUserManager):
    """
    A custom user manager to deal with emails as unique identifiers for auth
    instead of usernames. The default that's used is "UserManager"
    """
    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('The Email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)

Et le modèle User:

from Django.db import models
from Django.contrib.auth.models import AbstractBaseUser
from Django.contrib.auth.models import PermissionsMixin
from Django.utils.translation import ugettext_lazy as _


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True, null=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    USERNAME_FIELD = 'email'
    objects = MyUserManager()

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.email

    def get_short_name(self):
return self.email

Et enfin dans settings.py:

AUTH_USER_MODEL = ‘your_app_name.User’

Le générateur de jetons

La deuxième partie consiste à créer un générateur de jetons pour l'URL de confirmation de l'e-mail. Nous pouvons hériter du PasswordResetTokenGenerator intégré pour le rendre plus facile.

Créez tokens.py:

from Django.contrib.auth.tokens import PasswordResetTokenGenerator
from Django.utils import six

class TokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
            six.text_type(user.pk) + six.text_type(timestamp) +
            six.text_type(user.is_active)
        )

account_activation_token = TokenGenerator()

Le formulaire d'inscription

Ensuite, vous devez créer un formulaire d'inscription à utiliser dans nos vues. Le meilleur moyen est d'hériter du UserCreationForm intégré de Django et d'en supprimer les champs username et password, puis d'ajouter un champ email. forms.py:

from Django import forms
from Django.contrib.auth.forms import UserCreationForm
from Django.contrib.auth.models import User

class SignupForm(UserCreationForm):
    email = forms.EmailField(max_length=200, help_text='Required')

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name')

La vue d'inscription

Lors de l'inscription, vous devez rendre l'utilisateur inactif user.is_active = False Sans mot de passe set_unusable_password() jusqu'à ce que l'utilisateur termine l'activation. Nous allons également créer une URL d'activation et l'envoyer par e-mail à l'utilisateur après avoir terminé l'enregistrement.

dans views.py:

from Django.views import View
from Django.http import HttpResponse
from Django.shortcuts import render
from .forms import SignupForm
from Django.contrib.sites.shortcuts import get_current_site
from Django.utils.encoding import force_bytes
from Django.utils.http import urlsafe_base64_encode
from .tokens import account_activation_token
from Django.core.mail import EmailMessage


class Signup(View):
    def get(self, request):
        form = SignupForm()
        return render(request, 'signup.html', {'form': form})

    def post(self, request):
        form = SignupForm(request.POST)
        if form.is_valid():
            # Create an inactive user with no password:
            user = form.save(commit=False)
            user.is_active = False
            user.set_unusable_password()
            user.save()

            # Send an email to the user with the token:
            mail_subject = 'Activate your account.'
            current_site = get_current_site(request)
            uid = urlsafe_base64_encode(force_bytes(user.pk))
            token = account_activation_token.make_token(user)
            activation_link = "{0}/?uid={1}&token{2}".format(current_site, uid, token)
            message = "Hello {0},\n {1}".format(user.username, activation_link)
            to_email = form.cleaned_data.get('email')
            email = EmailMessage(mail_subject, message, to=[to_email])
            email.send()
            return HttpResponse('Please confirm your email address to complete the registration')

Et bien sûr, n'oubliez pas de créer un modèle pour votre vue d'inscription.


La vue d'activation

Ensuite, vous devez créer une vue pour que l'utilisateur active son compte en utilisant l'URL que nous avons créée dans la vue d'inscription. Nous utiliserons également le SetPasswordForm de Django intégré pour permettre aux utilisateurs de définir leurs mots de passe.

Dans views.py:

from Django.contrib.auth import get_user_model, login, update_session_auth_hash
from Django.contrib.auth.forms import PasswordChangeForm
from Django.utils.encoding import force_bytes, force_text
from Django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from .tokens import account_activation_token

User = get_user_model()

class Activate(View):
    def get(self, request, uidb64, token):
        try:
            uid = force_text(urlsafe_base64_decode(uidb64))
            user = User.objects.get(pk=uid)
        except(TypeError, ValueError, OverflowError, User.DoesNotExist):
            user = None
        if user is not None and account_activation_token.check_token(user, token):
            # activate user and login:
            user.is_active = True
            user.save()
            login(request, user)

            form = PasswordChangeForm(request.user)
            return render(request, 'activation.html', {'form': form})

        else:
            return HttpResponse('Activation link is invalid!')

    def post(self, request):
        form = PasswordChangeForm(request.user, request.POST)
        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user) # Important, to update the session with the new password
            return HttpResponse('Password changed successfully')

Encore une fois, n'oubliez pas de créer un modèle pour votre vue d'activation.


Les URL

Enfin, dans urls.py:

from . import views
from Django.urls import path

urlpatterns = [
    ...
    path('signup/', views.signup.as_view(), name='signup'),
    path('activate/<str:uid>/<str:token>', views.activate.as_view(), name='activate'),
]

P.S. Honnêtement, je n'ai pas encore eu l'occasion de tester toutes ces pièces ensemble, mais n'hésitez pas à demander si un problème s'est produit.

21
Peter Sobhi

En plus de la réponse de Peter, si vous utilisez Django 2, les parties d'encodage et de décodage sont un peu différentes.

Encodage:

Modifier 'uid': urlsafe_base64_encode(force_bytes(user.pk)),

À 'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),

Décodage:

Modifier uid = force_text(urlsafe_base64_decode(uidb64))

À uid = urlsafe_base64_decode(uidb64).decode()

1
Yogi Bear

Vous pouvez appeler le formulaire de réinitialisation du mot de passe en passant l'e-mail des utilisateurs à partir de la demande. Cela envoie le lien en utilisant l'authentification intégrée. Vous souhaiterez peut-être remplacer le modèle par défaut pour le formulaire de courrier électronique et de mot de passe.

from Django.contrib.auth.forms import PasswordResetForm

# either the request or domain_override kwarg is needed
form = PasswordResetForm({'email': user.email})
        if form.is_valid():
            return form.save(
                from_email='[email protected]',
                # domain_override='your_domain',
                request=request, 
                email_template_name='registration/password_reset_email.html'
            )

Je suis sûr que vous avez besoin d'un mot de passe existant pour que cela fonctionne afin que vous puissiez en créer un factice temporaire

password = User.objects.make_random_password()
user.set_password(password)
0
chaggy