Django用户登录认证遇双重问题:QuerySet报错及authenticate返回None
Django登录功能的两个问题排查与解决
问题描述
搭建网站登录功能时遇到两个问题:
- 自行编写登录逻辑时,触发报错:
AttributeError at /login/ , 'QuerySet' object has no attribute 'pk' - 改用Django官方
authenticate方法后,该方法始终返回None
自定义登录逻辑的views.py代码
... from django.contrib.auth import login as login_module def login(request): if request.method == "POST": Login = LoginFrom(request.POST) if Login.is_valid(): user = Login.cleaned_data if User.objects.filter(username=user['username']).exists(): user_info = User.objects.filter(username=user['username'] , password=user['password']).values('name') if user_info.exists(): login_module(request, user_info) messages.success(request, 'welcome {}'.format(user_info[0]['name'] ) , 'welcome_message') else: messages.error(request, 'wrong password', 'password_message') else: messages.error(request, 'username doesn\'t exist', 'username_message') else: Login = LoginFrom() return render(request, 'login.html')
使用官方authenticate方法的views.py代码
... from django.contrib.auth import authenticate from django.contrib.auth import login as login_module def login(request): if request.method == "POST": Login = LoginFrom(request.POST) if Login.is_valid(): cd = Login.cleaned_data user = authenticate(request, username = cd['username'], password = cd['password']) if user is not None: login_module(request, user) messages.success(request, 'login', 'welcome_message') else: messages.error(request, 'error (none)', 'error_message') else: Login = LoginFrom() return render(request, 'login.html')
共用的forms.py代码
class LoginFrom(forms.Form): username = forms.CharField() password = forms.CharField(widget=forms.PasswordInput)
login.html代码
<!DOCTYPE html> <html> <head><title>login</title></head> {% include 'header.html' %} <body class="background"> <form id="login_form" action="" method="POST"> {% csrf_token %} <p class="lead">login to your account</p> <div class="form-group"> <br> <div class="input-group"> <div class="input-group-text" id="basic-addon1"><i class="bi bi-people"></i></div> <input type="text" name="username" class="form-control" placeholder="Enter Your Username"required> </div> <div> {% if messages %} {% for message in messages %} {% if 'username_message' in message.tags %} <div class="text-danger" > {{message}} </div> {% endif %} {% endfor %} {% endif %} </div> </div> <div class="form-group"> <br> <div class="input-group"> <div class="input-group-text" id="basic-addon1"><i class="bi bi-key"></i></div> <input type="password" name="password" class="form-control" placeholder="Enter Your password" required> </div> <div> {% if messages %} {% for message in messages %} {% if 'password_message' in message.tags %} <div class="text-danger" > {{message}} </div> {% endif %} {% endfor %} {% endif %} </div> </div> <br> <button class="btn btn-outline-secondary" type="submit" name="login" value="login" >Login</button> <div> {% if messages %} {% for message in messages %} {% if 'welcome_message' in message.tags %} <div class="text-warning text-center" > {{message}} </div> {% endif %} {% endfor %} {% endif %} </div> </form> </body> </html>
自定义models.py代码
class User(models.Model): name = models.CharField(max_length=100) email = models.EmailField() username = models.CharField(max_length=100) password = models.CharField(max_length=100) comment_count = models.IntegerField(default=0)
问题原因与解决方法
第一个错误:AttributeError: 'QuerySet' object has no attribute 'pk'
- 原因:
login_module(request, user_info)中的user_info是filter()返回的QuerySet集合对象,而Django的login()方法要求传入单个User模型实例;同时你用了values('name'),返回的是字典集合,连模型实例都不是。 - 解决:
用get()获取单个用户实例,并去掉values(),修改后的代码片段:# 替换原user_info相关代码 try: user_info = User.objects.get(username=user['username'], password=user['password']) login_module(request, user_info) messages.success(request, f'welcome {user_info.name}', 'welcome_message') except User.DoesNotExist: messages.error(request, 'wrong password', 'password_message')
第二个问题:authenticate方法始终返回None
- 原因:
- 自定义的
User模型没有继承Django认证系统要求的基类,authenticate()默认调用Django自带的User模型,和你的自定义模型不兼容。 - 自定义模型中
password字段是明文存储,但authenticate()会自动对传入密码做哈希对比,明文永远匹配不上加密后的存储值。
- 自定义的
- 解决:
- 修改自定义User模型,继承认证基类并实现必要配置:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin class UserManager(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) return self.create_user(username, password, **extra_fields) class User(AbstractBaseUser, PermissionsMixin): name = models.CharField(max_length=100) email = models.EmailField(unique=True) username = models.CharField(max_length=100, unique=True) comment_count = models.IntegerField(default=0) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) objects = UserManager() USERNAME_FIELD = 'username' # 指定用用户名作为登录标识 REQUIRED_FIELDS = ['email'] # 创建超级用户时必填字段 - 在
settings.py中配置使用自定义User模型:AUTH_USER_MODEL = '你的应用名.User' # 替换为实际的app名称 - 重新迁移数据库:
python manage.py makemigrations python manage.py migrate - 创建用户必须用
User.objects.create_user()方法,确保密码被加密存储。
- 修改自定义User模型,继承认证基类并实现必要配置:
额外注意事项
- 表单类名
LoginFrom拼写错误,建议修正为LoginForm - 自定义User模型后,所有用户相关逻辑都要基于该模型,不要混用Django自带的User模型
内容的提问来源于stack exchange,提问作者yasin
相关产品推荐
相关产品推荐

