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

基于Django自定义表单的用户登录异常问题求助

问题:自定义用户模型登录失败(注册后可正常登录,单独登录报错)

问题描述

基于UserCreationForm和AuthenticationForm实现自定义注册、登录表单,用户注册后可立即自动登录,但单独进入登录页面输入正确账号密码时,始终返回错误:
{'invalid_login': '请输入正确的%(username)s和密码。注意两个字段都区分大小写。', 'inactive': '该账户未激活。'}

相关代码

models.py

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    profile_image = models.ImageField(upload_to='accounts/images', default='default/user.jpg', blank=True, null=True)
    address = models.CharField(max_length=130, null=True, blank=True)
    date_of_birth = models.DateField(null=True, blank=True)
    
    def __str__(self) -> str:
        return f"{self.username}, {self.email}"

forms.py

from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from .models import User
import django.forms as forms

class LoginForm(AuthenticationForm):

    class Meta:
        model = User
        fields = ['username', 'password']
        
    labels = {
        'username': "Username",
        'password': 'Password'
    }

class RegisterForm(UserCreationForm):
    profile_image = forms.ImageField(required=False)
    address = forms.CharField(max_length=130, required=False)
    date_of_birth = forms.DateField(required=False)

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2',
                  'profile_image', 'address', 'date_of_birth']

    labels = {
        'username': "Ім'я користувача",
        'email': 'Електронна пошта',
        'password1': 'Пароль',
        'password2': 'Підтвердження пароля',
        'profile_image': 'Ваше фото',
        'address': 'Адреса проживання',
        'date_of_birth': 'Дата народження'
    }

views.py

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.views import View
from .forms import RegisterForm, LoginForm

class RegisterView(View):

    def get(self, request):
        form = RegisterForm()
        return render(request, 'accounts/register.html', {'form': form})

    def post(self, request):
        form = RegisterForm(request.POST)
        if form.is_valid():
            user = form.save(commit=False)
            username = form.cleaned_data.get('username')
            raw_password = form.cleaned_data.get('password2')
            user.set_password(raw_password)
            user.save()
            
            authenticated_user = authenticate(username=username, password=raw_password)
            if authenticated_user is not None:
                login(request, authenticated_user)
                return redirect('blog:index')
            
        print(f"Form errors: {form.errors}")
        return render(request, "accounts/register.html", {'form': form})


class LoginView(View):
    
    def get(self, request):
        form = LoginForm()
        return render(request, 'accounts/login.html', {'form': form})

    def post(self, request):
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                print(f"User {username} login successfully.")
                return redirect('blog:index')
            
        print(f"Form errors: {form.error_messages}")
        return render(request, 'accounts/login.html', {'form': form})

login.html 模板

{% extends 'base.html' %}

{% block title %}
    Login user
{% endblock %}

{% block content %}
    <div>
        <form action="" method="post">
            {% csrf_token %}
            {{ form }}
            <br>
            <button type="submit">Log in</button>
        </form>
    </div>
{% endblock %}

原因分析

  1. LoginForm实例化错误:AuthenticationForm构造函数需要接收request作为第一个参数,原代码中LoginForm(request.POST)未传入request,导致表单内部验证逻辑(如用户状态检查、会话验证)无法正常执行。
  2. 重复验证冲突:原LoginView中手动调用authenticate,但AuthenticationForm本身已内置用户验证逻辑,重复操作可能导致状态不一致。
  3. LoginForm定义冗余:AuthenticationForm并非ModelForm,无需设置Meta类指定model和fields,冗余配置会干扰表单默认行为。

解决方案

1. 修正LoginForm定义

去掉冗余的Meta类,直接自定义字段标签:

class LoginForm(AuthenticationForm):
    username = forms.CharField(label="用户名")
    password = forms.CharField(label="密码", widget=forms.PasswordInput)

2. 修改LoginView的post方法

实例化LoginForm时传入request参数,利用表单内置验证逻辑:

class LoginView(View):
    
    def get(self, request):
        form = LoginForm()
        return render(request, 'accounts/login.html', {'form': form})

    def post(self, request):
        # 传入request和POST数据
        form = LoginForm(request, data=request.POST)
        if form.is_valid():
            # 通过表单的get_user()方法获取验证后的用户
            user = form.get_user()
            login(request, user)
            print(f"User {user.username} 登录成功")
            return redirect('blog:index')
        
        # 此时form.errors会包含具体错误信息,可传递给模板展示
        print(f"表单错误: {form.errors}")
        return render(request, 'accounts/login.html', {'form': form})

3. 确认settings.py配置

确保已指定自定义用户模型:

# settings.py
AUTH_USER_MODEL = 'accounts.User'

4. 可选:优化RegisterView(移除冗余代码)

UserCreationForm的save()方法已自动处理密码哈希,无需手动调用set_password:

class RegisterView(View):
    def post(self, request):
        form = RegisterForm(request.POST, request.FILES)  # 上传头像需要加上request.FILES
        if form.is_valid():
            user = form.save()
            login(request, user)  # 直接登录,无需再次authenticate
            return redirect('blog:index')
        
        print(f"表单错误: {form.errors}")
        return render(request, "accounts/register.html", {'form': form})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:38:19