This question specifically aims for a Django 2.0 answer as the registration
module isn't available (yet) for it.
这个问题专门针对Django 2.0的答案,因为注册模块尚不可用。
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.
更多,这看起来似乎很广泛,但我经常发现自己处于不能使用任何第三方模块的情况,因为......哦,好吧..政策。我相信很多人都这样做了。而且我知道从这里查看和汇总信息或django docs是令人头痛的问题。
Workflow:
Let's suppose we need the following flow:
我们假设我们需要以下流程:
- The user goes to the sign-up page and fills in the following fields:
first_name
,last_name
andemail
(the email will be used as the username). - 用户进入注册页面并填写以下字段:first_name,last_name和email(该电子邮件将用作用户名)。
- The user submits the form and receives a confirmation email with an URL containing a unique token.
- 用户提交表单并接收包含唯一令牌的URL的确认电子邮件。
- 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?
- 模型/视图(使用CBV)/表单/ URL如何显示?
2 个解决方案
#1
8
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.
首先,您需要创建自定义用户模型和自定义UserManager以删除用户名字段并改为使用电子邮件。
In models.py
the UserManager
should look like this:
在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)
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
:
最后在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.
第二部分是为电子邮件确认URL创建令牌生成器。我们可以继承内置的PasswordResetTokenGenerator以使其更容易。
Create tokens.py
:
创建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
:
然后,您应该创建一个注册表单以在我们的视图中使用。最好的方法是继承内置的Django的UserCreationForm并从中删除用户名和密码字段,然后添加一个电子邮件字段。 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.
在注册时,您应该使用户不活动user.is_active = False,没有密码set_unusable_password(),直到用户完成激活。此外,我们将构建激活URL并在完成注册后通过电子邮件将其发送给用户。
in views.py
:
在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.
然后,您应该为用户创建一个视图,以使用我们在注册视图中创建的URL激活其帐户。我们还将使用内置的Django的SetPasswordForm来允许用户设置他们的密码。
In views.py
:
在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
:
最后,在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.
附:老实说,我没有机会一起测试所有这些部分,但不要犹豫,问是否有任何问题发生。
#2
0
You can call the Password reset form passing the users email from the request. That sends the link using the built-in auth. You may want to replace the default template for the email and password form.
您可以调用密码重置表单,从请求中传递用户电子邮件。这使用内置的auth发送链接。您可能希望替换电子邮件和密码表单的默认模板。
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@email.com',
# domain_override='your_domain',
request=request,
email_template_name='registration/password_reset_email.html'
)
Pretty sure you need an existing password for this to work so you can create a temp dummy one
很确定你需要一个现有的密码才能工作,所以你可以创建一个临时虚拟密码
password = User.objects.make_random_password()
user.set_password(password)
#1
8
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.
首先,您需要创建自定义用户模型和自定义UserManager以删除用户名字段并改为使用电子邮件。
In models.py
the UserManager
should look like this:
在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)
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
:
最后在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.
第二部分是为电子邮件确认URL创建令牌生成器。我们可以继承内置的PasswordResetTokenGenerator以使其更容易。
Create tokens.py
:
创建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
:
然后,您应该创建一个注册表单以在我们的视图中使用。最好的方法是继承内置的Django的UserCreationForm并从中删除用户名和密码字段,然后添加一个电子邮件字段。 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.
在注册时,您应该使用户不活动user.is_active = False,没有密码set_unusable_password(),直到用户完成激活。此外,我们将构建激活URL并在完成注册后通过电子邮件将其发送给用户。
in views.py
:
在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.
然后,您应该为用户创建一个视图,以使用我们在注册视图中创建的URL激活其帐户。我们还将使用内置的Django的SetPasswordForm来允许用户设置他们的密码。
In views.py
:
在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
:
最后,在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.
附:老实说,我没有机会一起测试所有这些部分,但不要犹豫,问是否有任何问题发生。
#2
0
You can call the Password reset form passing the users email from the request. That sends the link using the built-in auth. You may want to replace the default template for the email and password form.
您可以调用密码重置表单,从请求中传递用户电子邮件。这使用内置的auth发送链接。您可能希望替换电子邮件和密码表单的默认模板。
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@email.com',
# domain_override='your_domain',
request=request,
email_template_name='registration/password_reset_email.html'
)
Pretty sure you need an existing password for this to work so you can create a temp dummy one
很确定你需要一个现有的密码才能工作,所以你可以创建一个临时虚拟密码
password = User.objects.make_random_password()
user.set_password(password)