页面模板
新建\myblog\templates\user\register.html
{% extends 'base.html' %}
{% load static %}
{% block title %}注册{% endblock %}
{% block main %}
<div class="container">
<form class="form-signin bg-white" method="post" action="{% url 'system:register' %}" id="register-form">
<div class="text-center mb-4">
<img class="mb-2" src="{% static '/images/favicon.png' %}" alt="" width="100" height="100">
<h1 class="h3 mb-3 font-weight-normal">欢迎注册本博客</h1>
<div class="rollContainer">
<p class="rollBlock">友情提示:请使用QQ邮箱完成站点注册,谢谢!</p>
</div>
<div class="login_error">
{% for k,v in register_form.errors.items %}
{{ v }}
{% endfor %}
</div>
</div>
<div class="form-label-group">
<input type="text" id="inputUsername" name="username"
value="{% if register_form.username.value %}{{ register_form.username.value }}{% endif %}"
class="form-control {% if register_form.errors.username %}is-invalid{% endif %}"
placeholder="用户名" aria-describedby="usernameHelpBlock" required autofocus>
<label for="inputEmail">用户名</label>
<small id="usernameHelpBlock" class="form-text text-muted">
用户名只能包括中文,字母,数字,长度限制在2—15位
</small>
</div>
<div class="form-label-group">
<input type="email" id="inputEmail" name="email"
value="{% if register_form.email.value %}{{ register_form.email.value }}{% endif %}"
class="form-control {% if register_form.errors.email %}is-invalid{% endif %}" placeholder="邮箱"
aria-describedby="emailHelpBlock" required>
<label for="inputEmail">邮箱</label>
<small id="emailHelpBlock" class="form-text text-muted">
请输入你的邮箱
</small>
</div>
<div class="form-label-group">
<input type="password" id="inputPassword" name="password"
value="{% if register_form.password.value %}{{ register_form.password.value }}{% endif %}"
class="form-control {% if register_form.errors.password %}is-invalid{% endif %}" placeholder="密码"
aria-describedby="passwordHelpBlock" required>
<label for="inputPassword">密码</label>
<small id="passwordHelpBlock" class="form-text text-muted">
密码必须在6-18之间,允许包括字母,数字以及<span style="font-size: small;font-weight: bold">.@+-_?&%$</span>>这些字符组合
</small>
</div>
<div class="form-label-group">
<input type="password" id="inputPassword" name="repassword"
value="{% if register_form.repassword.value %}{{ register_form.repassword.value }}{% endif %}"
class="form-control {% if register_form.errors.repassword %}is-invalid{% endif %}"
placeholder="重复密码" aria-describedby="repasswordHelpBlock" required>
<label for="inputPassword">重复密码</label>
</div>
<div class="form-label-group">
{{ register_form.captcha }}
</div>
<small class="form-text text-muted my-3">
注册完毕,激活邮件将会发送到您的邮箱,还望查收。
</small>
<button class="btn btn-lg btn-success btn-block" type="submit">注册</button>
{% csrf_token %}
</form>
<div class="clearfix " style="width: 100%;max-width: 420px;padding: 15px;margin: auto;">
<p class="float-left"><a class="text-muted " href="#">←回到首页</a></p>
<p class="float-right">
<a class="text-muted " href="#">登录</a>
|
<a class="text-muted " href="#">忘记密码</a>
</p>
</div>
</div>
{% endblock %}
注册激活邮件
编辑\myblog\system\tools.py
内容如下,RegisterActiveEmailSender继承之前封装的EmailSender,重写了get_template_context函数,实现了注册激活需要的参数逻辑。
from django.urls import reverse
from django.utils.crypto import get_random_string
from system.models import EmailRecord, User
from django.core.mail import send_mail
from django.template.loader import render_to_string
from myblog.email_settings import EMAIL_FROM
from myblog.settings import SITE_URL, SITE_NAME
import logging
logger = logging.getLogger(__name__)
...
"""
注册激活邮件发送器
"""
class RegisterActiveEmailSender(EmailSender):
template_name = "user/email-register-active.html"
def get_template_context(self, password=""):
context = super(RegisterActiveEmailSender, self).get_template_context()
self.record.url = self.site_url + reverse(viewname='sys:register_active', kwargs={'code': self.record.code})
context.update({"url": self.record.url, "password": password})
return context
注册激活邮件任务
新增\myblog\system\tasks.py
添加以下内容,需要注意这里文件名必须为tasks.py,便于后面celery的自动任务发现机制。可以看到下面发送邮件是区分了两种环境的。
如果是DEBUG=False(生产环境)的情况下则是交给celery异步发送邮件,同时在之前prod_settings.py里面已经配置过 EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
。
而在DEBUG=True(开发环境)则是同步发送且因为在dev_settings.py里面定义了
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
,邮件直接输出在控制台,便于开发调试。
from celery import shared_task
from django.conf import settings
from system.models import EmailType
from system.tools import RegisterActiveEmailSender
import logging
logger = logging.getLogger("django")
@shared_task
def _send_email(email_type=None, username=None, *args, **kwargs):
if email_type == EmailType.ACTIVE:
RegisterActiveEmailSender(EmailType.ACTIVE, username).send(*args, **kwargs)
else:
pass
def send_email(email_type=None, username=None, *args, **kwargs):
if settings.DEBUG:
_send_email(email_type, username, *args, **kwargs)
else:
_send_email.delay(email_type, username, *args, **kwargs)
注册表单
编写\myblog\system\forms.py
添加以下内容,需要注意这里面使用到了django-simple-captcha依赖,如下面的
from captcha.fields import CaptchaField
,这个在之前初始化工程的时候已经安装了开发环境下需要的依赖了。
import logging
from captcha.fields import CaptchaField
from django import forms
from django.core import validators
from system.models import User
logger = logging.getLogger('django')
class UserRegisterForm(forms.Form):
email = forms.EmailField(required=True, max_length=50, error_messages={'require': "请输入邮箱地址"})
username = forms.CharField(required=True, error_messages={'require': "请输入用户名"}, max_length=30, min_length=2,
validators=[
validators.RegexValidator(regex=r'^[\u4E00-\u9FA5A-Za-z0-9]{2,15}$',
message="用户名只能包括中文,字母,数字")])
password = forms.CharField(required=True, error_messages={'require': "请输入密码"}, min_length=6, max_length=18,
validators=[
validators.RegexValidator(regex=r'[\w.@+-_?&%$)!]{6,18}$',
message="请输入合法的密码")])
repassword = forms.CharField(required=True, error_messages={'require': "请再次输入密码"}, min_length=6, max_length=18,
validators=[
validators.RegexValidator(regex=r'[\w.@+-_?&%$)!]{6,18}$',
message="请输入合法的密码")])
captcha = CaptchaField(required=True, error_messages={'invalid': '验证码输入错误!'})
def clean_email(self):
email = self.cleaned_data.get('email', '')
exists = User.objects.filter(email=email).exists()
if exists:
logger.info("【{email}】邮箱已经存在!".format(email=email))
raise forms.ValidationError("邮箱已经存在!")
return email
def clean_username(self):
username = self.cleaned_data.get('username', '')
exists = User.objects.filter(username=username).exists()
if exists:
logger.info("【{username}】用户名已存在!".format(username=username))
raise forms.ValidationError("该用户名已存在!")
return username
def clean(self):
clean_data = super().clean()
password = self.cleaned_data.get('password', '')
repassword = self.cleaned_data.get('repassword', '')
if password != repassword:
logger.info("两次密码不一致!")
raise forms.ValidationError(message="两次密码不一致!")
return clean_data
注册视图和注册激活视图
编写\myblog\system\views.py
添加以下内容
import logging
from django.db import transaction
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views.generic.base import View
from system import forms
from system.models import User, EmailType
from system.tasks import send_email
logger = logging.getLogger('django')
class UserRegisterView(View):
def get(self, request):
register_form = forms.UserRegisterForm()
return render(request, "user/register.html", {'register_form': register_form})
@transaction.atomic
def post(self, request):
register_form = forms.UserRegisterForm(request.POST)
if register_form.is_valid():
email = register_form.cleaned_data['email']
username = register_form.cleaned_data['username']
password = register_form.cleaned_data['password']
User.objects.create_user(username=username, email=email, password=password)
send_email(EmailType.ACTIVE, username, password)
return redirect(reverse("sys:login"))
else:
logger.info("注册表单验证未通过")
return render(request, "user/register.html", {'register_form': register_form})
class UserActiveView(View):
def get(self, request, code):
record = EmailRecord.objects.filter(code=code).first()
if record:
receiver = record.receiver
receiver.is_active = True
receiver.save()
return render(reverse("sys:login"))
路由绑定
编辑\myblog\system\urls.py
修改成以下内容
from django.urls import path
from system import views
urlpatterns = [
path('register', views.UserRegisterView.as_view(), name='register'),
path('active/<str:code>',views.UserActiveView.as_view(), name='register_active'),
]