django搭建博客八用户登录

2022年05月22日 0评论 406阅读 0喜欢

页面模板

新建\myblog\templates\user\login.html

{% extends 'base.html' %}
{% load static %}
{% block title %}登陆{% endblock %}
{% block main %}
    <!--主内容-start--->
    <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'),
]

发表评论 取消回复

电子邮件地址不会被公开。

请输入以http或https开头的URL,格式如:https://oneisall.top