继续对老师的教程表示难以理解,全部从头自己写意味着又繁琐又不安全,更何况我觉得已经背离了初衷,你明明是django的教程,那就应该教大家如何使用django自带的模块实现想要的功能,而不是自己重复造一堆漏洞百出的轮子。
总体思路是这样的,先写一个登录界面,然后通过编写一个中间件确保用户在登录状态下才能访问除指定页面以外的其他界面(换句话来说,使用中间件可以提供全局的登录检查,创建一个自定义中间件可以确保用户在访问某些视图之前已经登录)
1.首先,在urls.py中创建一个URLpath('login/', account.login_view)
(但其实应该是path('login/', account.login_view, name='login'),
至于具体原因后面再细说)
2.对之前创建在数据库中的表进行更改
在此之前,models.py
中Admin
类像其他的一样,继承自 models.Model
,但实际上这个是有问题的。django针对这方面明明有写好的类。
class Admin(AbstractBaseUser):
username = models.CharField(verbose_name='用户名', max_length=32, unique=True)
password = models.CharField(verbose_name='密码', max_length=255)
修改后:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class AdminManager(BaseUserManager):
def create_user(self, username, password=None, **extra_fields):
"""
创建并返回一个普通用户,具有给定的用户名、电子邮件和密码。
"""
if not username:
raise ValueError('必须设置用户名')
user = self.model(username=username, **extra_fields)
user.set_password(password) # 正确处理密码加密
user.save(using=self._db)
return user
def create_superuser(self, username, password=None, **extra_fields):
"""
创建并返回一个超级用户,具有给定的用户名、电子邮件和密码。
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('超级用户必须设置is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('超级用户必须设置is_superuser=True.')
return self.create_user(username, password, **extra_fields)
class Admin(AbstractBaseUser):
username = models.CharField(verbose_name='用户名', max_length=32, unique=True)
password = models.CharField(verbose_name='密码', max_length=255)
USERNAME_FIELD = 'username'
objects = AdminManager() # 指定自定义的管理器
def __str__(self):
return self.username
gpt4讲解:
导入部分
from django.contrib.auth.models import
AbstractBaseUser, BaseUserManager
这行代码从Django的认证模块导入了两个类:AbstractBaseUser
和BaseUserManager
。AbstractBaseUser
提供了用户模型的核心实现,而BaseUserManager
是用于管理这种用户对象的类,尤其是创建用户和超级用户。
AdminManager类
class AdminManager(BaseUserManager):
AdminManager
类继承自BaseUserManager
。这意味着AdminManager
会拥有BaseUserManager
的所有方法,并且你可以添加或重写方法来定制管理用户的方式。
create_user方法
def create_user(self, username, password=None, **extra_fields):
这个方法用于创建一个普通用户。它接收用户名、密码和任意数量的附加字段作为参数。方法的第一部分检查是否提供了用户名:
if not username:
raise ValueError('必须设置用户名')
如果没有提供用户名,则抛出一个ValueError
异常。
然后,使用self.model
(这是AdminManager
关联的模型类,即后面定义的Admin
类)创建用户对象,并设置密码:
user = self.model(username=username, **extra_fields)
user.set_password(password) # 正确处理密码加密
set_password
方法用于加密密码,以安全方式保存。
最后,保存用户对象:
user.save(using=self._db)
并返回这个用户对象。
create_superuser方法
def create_superuser(self, username, password=None, **extra_fields):
这个方法用于创建一个超级用户。它首先设置is_staff
和is_superuser
字段为True
:
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
然后,检查这些字段确实被设置为True,否则抛出异常。最后,调用create_user方法创建超级用户。
Admin类
class Admin(AbstractBaseUser):
Admin类继承自AbstractBaseUser,表示一个自定义的用户模型。
字段定义
username = models.CharField(verbose_name='用户名', max_length=32, unique=True)
password = models.CharField(verbose_name='密码', max_length=255)
这里定义了用户名和密码两个字段,使用Django的模型字段类型CharField
。
USERNAME_FIELD
USERNAME_FIELD = 'username'
这是AbstractBaseUser要求指定的一个类属性,表明用作唯一标识符的字段是username。
objects属性
objects = AdminManager() # 指定自定义的管理器
这里将AdminManager
实例化并赋值给objects
,这样Django就知道对于Admin
模型应该使用AdminManager
来管理对象。
总结
这段代码展示了如何在Django中通过继承AbstractBaseUser
和BaseUserManager
来创建一个自定义的用户模型和管理器。通过这种方式,你可以添加额外的字段和方法来满足特定的业务需求,同时还能利用Django内建的认证系统的强大功能。
3.登录界面的编写
from django.contrib.auth import authenticate, login
from django.core.exceptions import MultipleObjectsReturned
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import check_password
from django import forms
from app01 import models
class LoginForm(forms.Form):
username = forms.CharField(label='用户名', max_length=16)
# password = forms.PasswordInput()
password = forms.CharField(label='密码', widget=forms.PasswordInput)
def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
try:
# 使用 Django 的 authenticate 方法验证用户名和密码
user = authenticate(request, username=username, password=password)
print("DEBUG: User returned by authenticate:", user) # 调试信息
if user is not None:
login(request, user) # 如果认证成功,执行登录操作
return redirect('/admin/list')
else:
form.add_error(None, '用户名或密码错误')
except models.Admin.DoesNotExist:
form.add_error(None, '用户名或密码错误')
return render(request, 'login.html', {'form': form})
except MultipleObjectsReturned:
form.add_error(None, '发生了一个内部错误。请联系管理员。')
return render(request, 'login.html', {'form': form})
return render(request, 'login.html', {'form': form})
else:
form = LoginForm()
return render(request, 'login.html', {'form': form})
4.前端登录界面的编写
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-5.3.0-alpha1-dist/css/bootstrap.css' %}">
</head>
<body>
{% load widget_tweaks %}
<div class="container">
<div class="row">
<div class="col-sm-6 offset-sm-3">
<h2 class="text-center" style="margin-top: 15%">登录</h2>
<form method="post" class="mt-4">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<!-- 渲染表单字段并应用 Bootstrap 样式 -->
<div class="form-group">
{{ form.username.label_tag }}
{{ form.username|add_class:'form-control' }}
</div>
<div class="form-group">
{{ form.password.label_tag }}
{{ form.password|add_class:'form-control' }}
</div>
<button type="submit" class="btn btn-primary btn-block" style="margin-top: 10px">登录</button>
</form>
</div>
</div>
</div>
<script src="{% static 'js/popper.min.js' %}"></script>
<script src="{% static 'js/jquery-3.7.1.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-5.3.0-alpha1-dist/js/bootstrap.js' %}"></script>
</body>
</html>
5.处理登录请求
from django.shortcuts import redirect
from django.urls import reverse
from django.conf import settings
class LoginRequiredMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 检查用户是否登录,并且请求的不是登录页面
if not request.user.is_authenticated and not request.path.startswith(reverse('login')):
# 重定向到登录页面,可以传递当前路径作为next参数
return redirect(f"{settings.LOGIN_URL}?next={request.path}")
# 如果用户已经登录,或者正在访问登录页面,正常处理请求
response = self.get_response(request)
return response
6.settings.py中的配置
确保在settings.py中正确设置了AUTH_USER_MODEL,以指向自定义用户模型。
AUTH_USER_MODEL = 'app01.Admin'