You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django用户登录认证遇双重问题:QuerySet报错及authenticate返回None

Django登录功能的两个问题排查与解决

问题描述

搭建网站登录功能时遇到两个问题:

  1. 自行编写登录逻辑时,触发报错:AttributeError at /login/ , 'QuerySet' object has no attribute 'pk'
  2. 改用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

  • 原因:
    1. 自定义的User模型没有继承Django认证系统要求的基类,authenticate()默认调用Django自带的User模型,和你的自定义模型不兼容。
    2. 自定义模型中password字段是明文存储,但authenticate()会自动对传入密码做哈希对比,明文永远匹配不上加密后的存储值。
  • 解决:
    1. 修改自定义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']  # 创建超级用户时必填字段
      
    2. 在settings.py中配置使用自定义User模型:
      AUTH_USER_MODEL = '你的应用名.User'  # 替换为实际的app名称
      
    3. 重新迁移数据库:
      python manage.py makemigrations
      python manage.py migrate
      
    4. 创建用户必须用User.objects.create_user()方法,确保密码被加密存储。

额外注意事项

  • 表单类名LoginFrom拼写错误,建议修正为LoginForm
  • 自定义User模型后,所有用户相关逻辑都要基于该模型,不要混用Django自带的User模型

内容的提问来源于stack exchange,提问作者yasin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 20:55:20