Django 2-如何使用电子邮件确认和CBV注册用户? [英] Django 2 - How to register a user using email confirmation and CBVs?

查看:100
本文介绍了Django 2-如何使用电子邮件确认和CBV注册用户?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题专门针对Django 2.0答案,因为registration模块尚不可用.

更多,这似乎范围很广,但是我经常发现自己处于无法使用任何第三者模块的情况,因为...哦.政策.我敢肯定有很多.而且我知道查找和汇总从此处或django文档获取的信息是一件令人头疼的事情.


工作流程:

假设我们需要以下流程:

  1. 用户转到注册页面并填写以下字段:first_namelast_nameemail(电子邮件将用作用户名).
  2. 用户提交表单并收到一封确认电子邮件,其中包含包含唯一令牌的URL.
  3. 当用户单击所接收的链接时,他将被重定向到一个页面,在该页面中将设置密码.完成后,他已登录到仪表板页面.

其他信息:用户稍后将使用他的电子邮件(实际上是他的用户名)和密码登录.


具体问题:

  • 模型/视图(使用CBV)/表单/网址的外观如何?

解决方案

用户模型

首先,您需要创建一个自定义的User模型和一个自定义的UserManager来删除username字段并改用email.

models.py中,UserManager应该看起来像这样:

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)

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

最后是settings.py:

AUTH_USER_MODEL = ‘your_app_name.User’


令牌生成器

第二部分是为电子邮件确认URL创建令牌生成器.我们可以继承内置的PasswordResetTokenGenerator来简化操作.

创建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()


注册表格

然后,您应该创建一个注册表单以在我们的视图中使用.最好的方法是继承内置的Django的UserCreationForm并从中删除usernamepassword字段,然后添加一个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')


注册视图

在注册时,应使用户在没有密码set_unusable_password()的情况下处于非活动状态user.is_active = False,直到用户完成激活为止.另外,我们将构造一个激活URL,并在完成注册后通过电子邮件将其发送给用户.

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')

当然,不要忘记为您的注册视图创建模板.


激活视图

然后,您应该使用用户在注册视图中创建的URL创建一个视图,以供用户激活其帐户. 我们还将使用内置的Django SetPasswordForm允许用户设置密码.

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')

同样,不要忘记为您的激活视图创建模板.


URL

最后,在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.老实说,我还没有机会一起测试所有这些部分,但是却毫不犹豫地询问是否发生了任何问题.

This question specifically aims for a Django 2.0 answer as the registration module isn't available (yet) for it.

More, this might seem to broad, but I often found myself in situations where I can't use any 3rd party module because ... oh well..policies. I'm sure many did. And I know that looking and putting together information taken from here or django docs was a headache.


Workflow:

Let's suppose we need the following flow:

  1. The user goes to the sign-up page and fills in the following fields: first_name, last_name and email (the email will be used as the username).
  2. The user submits the form and receives a confirmation email with an URL containing a unique token.
  3. When the user clicks on the received link, he's redirected to a page where he'll set his password. When done, he's logged in to the dashboard page.

Extra-info: The user will later log in by using his email (which is actually his username) and password.


Specific question:

  • How will the models/views (using CBVs)/forms/urls look like?

解决方案

The User Model

First, you will need to create a custom User model and a custom UserManager to remove the username field and use email instead.

In models.py the UserManager should look like this:

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)

And the User model:

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

And finally in settings.py:

AUTH_USER_MODEL = ‘your_app_name.User’


The Token Generator

Second part is to create a token generator for the email confirmation url. We can inherit the built-in PasswordResetTokenGenerator to make it easier.

Create 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()


The Signup Form

Then you should create a registration form to use in our views. Best way is to inherit the built-in Django's UserCreationForm and to remove the username and password fields from it and then add an email field. 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')


The Signup View

In the sign up you should make the user inactive user.is_active = False with no password set_unusable_password() until the user complete the activation. Also, we are going to construct an activation URL and email it to the user after completing the registration.

in 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')

And of course, don't forget to create a template for you signup view.


The Activation View

Then you should create a view for the user to activate his account using the URL we created in the sign up view. We will also use the built-in Django's SetPasswordForm to allow users to set their passwords.

In 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')

Again, don't forget to create a template for your activation view.


The URLs

Finally, in 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. Honestly, I didn't get a chance to test all this parts together yet but don't hesitate to ask if any problem happened.

这篇关于Django 2-如何使用电子邮件确认和CBV注册用户?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆