一. 登陆
# 1. 需要依赖中间件
# 2. 需要依赖工具类-code.py
# 3. 需要依赖工具类-bootstrap.py
# 4. 需要依赖工具类-md5_data.py
# 5. 将APP中的views.py删除, 创建 views文件夹-->login.py
二. 前置:session和cookie
登陆成功后获取:
    cookie随机字符串
    session用户信息
# 在其他需要登陆才能访问的页面中,都需要加入验证
# 检查用户是否已登陆,已登陆可以继续向下走,未登录,跳转登陆页面
# 用户发来请求,获取cookie随机字符串,拿着随机字符串查看session中有没有
# request.session["info"]  # 不存在会报错
目标: 在所有的视图函数前面统一加上判断(太麻烦,使用中间件解决)
    info = request.session.get("info")
    if not info:
        return redirect("/login/")
可以直接在模板中使用设置的session
       request.session["info"] = {"id": admin_object.id, "name": admin_object.username}  # 会自动生成cookie和在服务器生成session
       request.session.set_expiry(60 * 60 * 24 * 7)  # 设置session时长-7天免登录
       {{ request.session.info.name }}
三. APP-models.py创建管理员表
from django.db import models
class Admin(models.Model):
    """管理员表"""
    username = models.CharField(verbose_name="用户名", max_length=32)
    password = models.CharField(verbose_name="密码", max_length=64)
    # 关联的字段是一个对象,需要加上__str__返回字段
    def __str__(self):
        return self.username
# 执行
python manage.py makemigrations
python manage.py migrate
四. 编写url
from bbc_list.views import login
urlpatterns = [
    path('login/', login.login),  # 登录页的url
    path("image/code/", login.image_code),  # 图片验证码的url
]
五. 编写视图函数(APP-->views-->login.py)
from django import forms
from django.http import HttpResponse
from django.shortcuts import render, redirect
# 使用Form组件
from bbc_list import models
from bbc_list.utils.bootstrap import BootStrapForm
from bbc_list.utils.md5_data import md5
from bbc_list.utils.code import check_code
class LoginForm(BootStrapForm):
    # form需要自定义字段
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput,
        required=True  # required=True: 必填
    )
    password = forms.CharField(label="密码",
                               widget=forms.PasswordInput(render_value=True),  # render_value=True:在输入框保留用户输入的内容
                               required=True)
    code = forms.CharField(label="验证码",
                           widget=forms.TextInput,
                           required=True)
    # 定义一个钩子方法获取密码
    def clean_password(self):
        # # cleaned_data获取用户输入的内容: {'username': '123', 'password': '321'}
        pwd = self.cleaned_data.get("password")
        return md5(pwd)
#  使用modelfprm
# class LoginModelForm(forms.ModelForm):
# modelform需要去数据库里拿数据
#     class Meta:
#         model = models.Admin
#         fields = ["username", "password"]
def login(request):
    """登陆"""
    if request.method == "GET":
        form = LoginForm()
        return render(request, "login.html", {"form": form})
    form = LoginForm(data=request.POST, )
    if form.is_valid():
        # 验证成功,获取到用户名和密码
        # {'username': '123', 'password': '321',"code":"xxx"}
        # print(form.cleaned_data)  # form验证通过后只能form.cleaned_data验证
        user_input_code = form.cleaned_data.pop("code")
        code = request.session.get("img_code", "")
        print("=========", user_input_code, code)
        if code.upper() != user_input_code.upper():
            form.add_error("code", "验证码错误")
            return render(request, "login.html", {"form": form})
        # 去数据库效验用户名和密码是否正确,获取用户对象,如果错的就是None
        # filter(username="form.cleaned_data["username]",password="form.cleaned_data["password]")
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        print("adin_object=", admin_object)
        if not admin_object:
            # form / modelform可以 主动的 手动添加错误信息,(字段名,错误信息)
            form.add_error("password", "用户名或密码错误")
            return render(request, "login.html", {"form": form})
        # 用户名和密码输入正确
        # 网站生成随机字符串,写到用户浏览器的cookie中,在写入到seesion中
        request.session["info"] = {"id": admin_object.id, "name": admin_object.username}  # 会自动生成cookie和在服务器生成session
        request.session.set_expiry(60 * 60 * 24 * 7)  # 7天免登陆
        return redirect("/admin/list/")  # 登陆成功后跳转的页面
    return render(request, "login.html", {"form": form})
from io import BytesIO
def image_code(request):
    """生成验证码图片"""
    # 调用code函数
    img, code_string = check_code()
    # print("img-code=", img, code_string)
    # 写入到session中,以便于后续验证
    request.session["img_code"] = code_string
    # 设置验证码60秒超时
    request.session.set_expiry(60)
    # 创建实力对象
    stream = BytesIO()
    img.save(stream, "png")  # 将图片保存到内存中
    return HttpResponse(stream.getvalue())  # 再从内存获取图片
def logout(request):
    """注销"""
    # 清除当前session
    request.session.clear()
    return redirect("/login/")
六. 编写html(APP-->templates-->login.html)
{% load static %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.min.css' %}">
    <style>
        .account{
            width:400px;
            border: 1px solid #dddddd;
            border-radius: 5px;
            box-shadow: 5px 5px 20px #aaa;
            margin-left:auto;
            margin-right:auto;
            margin-top:100px;
            padding:20px 40px;
                    }
        .account h2{
            margin-top:10px;
            text-align:center;
                        }
    </style>
</head>
<body>
<div class="account">
    <h2>用户登陆</h2>
    <form method="post" novalidate>
        {% csrf_token %}
        <div class="form-group">
            <label>用户名</label>
            {{ form.username }}
            <span style="color:red;">{{ form.username.errors.0}}</span>
        </div>
        <div>
            <div class="form-group">
                <label>密码</label>
                {{ form.password }}
                <span style="color:red;">{{ form.password.errors.0}}</span>
            </div>
        </div>
        <div class="form-group">
            <label>图片验证</label>
            <div class="row">
                <div class="col-xs-7">
                    {{ form.code}}
                    <span style="color:red;">{{ form.code.errors.0 }}</span>
                </div>
                <div class="col-xs-5">
                    <img id="image_code" src="/image/code/" style="width:125px;">
                </div>
            </div>
        </div>
        <input type="submit" value="登陆" class="btn btn-primary">
    </form>
</div>
</body>
</html>