页面模板
新建\myblog\templates\user\login.html
{% extends 'base.html' %}
{% load static %}
{% block title %}登陆{% endblock %}
{% block main %}
<div class="container">
<form class="form-signin bg-white" method="post" action="{% url 'sys:login' %}">
<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 login_form.errors.items %}
{{ v }}
{% endfor %}
</div>
</div>
<div class="form-label-group">
<input type="text" id="inputUsername" name="username"
value="{% if login_form.username.value %}{{ login_form.username.value }}{% endif %}"
class="form-control {% if login_form.errors.username %}is-invalid{% endif %}" placeholder="用户名"
required autofocus>
<label for="inputUsername">用户名或邮箱</label>
</div>
<div class="form-label-group">
<input type="password" id="inputPassword" name="password"
value="{% if login_form.password.value %}{{ login_form.password.value }}{% endif %}"
class="form-control {% if login_form.errors.password %}is-invalid{% endif %}" placeholder="密码"
required>
<label for="inputPassword">密码</label>
</div>
<div class="checkbox mb-3 text-muted">
<label class="pull-left">
<input type="checkbox" value="remember-me">下次自动登录
</label>
<a class="text-muted pull-right " href="">忘记密码?去重置</a>
</div>
<input type="hidden" name="next_to" value="{{ next_to }}">
<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="{% url 'index' %}">←回到首页</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 django import forms
from django.contrib.auth.validators import validators
from django.db.models import Q
import logging
from system.models import User
logger = logging.getLogger('django')
class UserLoginForm(forms.Form):
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,20}$',
message="用户名只能包括中文,数字以及符号@.")])
password = forms.CharField(required=True, error_messages={'require': "请输入密码"}, min_length=5, max_length=18,
validators=[validators.RegexValidator(regex=r'[\w.@+-_?&%$)!]{6,18}$',
message="请输入合法的密码")])
def clean_username(self):
username = self.cleaned_data.get('username', '')
try:
user = User.objects.get(Q(username=username) | Q(email=username))
if not user.is_active:
raise forms.ValidationError("用户未激活")
except User.DoesNotExist:
logger.info("【{username}】用户名或者邮箱不存在!".format(username=username))
raise forms.ValidationError("用户名或者邮箱不存在!")
return username
def clean(self):
clean_data = super().clean()
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if not username is None and password is not None:
try:
user = User.objects.get(Q(username=username) | Q(email=username))
if not user.check_password(password):
logger.info("【{username}】登陆密码不正确!".format(username=username))
raise forms.ValidationError("登陆密码不正确")
except User.DoesNotExist:
pass
return clean_data
登陆和登出视图
编写\myblog\system\views.py
添加以下内容
import logging
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
from django.views.generic.base import View
from system import forms
logger = logging.getLogger('django')
class UserLoginView(View):
def get(self, request):
return render(request, "user/login.html", {'next_to': request.GET.get('next_to', '/')})
def post(self, request):
next_to = request.POST.get('next_to', '/')
login_form = forms.UserLoginForm(request.POST)
if login_form.is_valid():
username = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
user = authenticate(username=username, password=password)
login(request, user)
logger.info("用户{username}登录成功".format(username=username))
return redirect(next_to)
logger.info("登陆表单验证未通过")
return render(request, "user/login.html", {'login_form': login_form, 'next_to': next_to})
class UserLoginOutView(View):
def get(self, request):
next_to = request.GET.get('next_to', '/')
logout(request)
logger.info("用户{username}退出登录".format(username=request.user.username))
return redirect(next_to)
路由绑定
编辑\myblog\system\urls.py
添加以下内容
from django.urls import path
from system import views
urlpatterns = [
path('login', views.UserLoginView.as_view(), name='login'),
path('logout', views.UserLoginOutView.as_view(), name='logout'),
]
|