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

Django登录提交后无法跳转详情页,提示密码无效求助

问题描述

提交正确的登录邮箱与密码后,页面无任何跳转,仅提示“Invalid password”。已确认可直接访问http://127.0.0.1:8000/user/2/查看用户详情页,怀疑登录视图或认证逻辑存在问题,但无法准确定位。

项目代码

views.py

import random
from django.shortcuts import render
from django.views.generic import (ListView, DetailView)
from django.views.decorators.csrf import csrf_exempt
from .models import CustomUser, Events
from rest_framework.views import APIView
from .serializers import CustomUserSerializer, EventsSerializer
from rest_framework.response import Response
from rest_framework import status
from rest_framework.renderers import TemplateHTMLRenderer
from django.contrib.auth import authenticate, login
from django.contrib import messages
from django.shortcuts import redirect, render
from django.urls import reverse
from .models import CustomUser

def index(request):
    return render(request, "users_application/index.html", {})

def user_detail(request, id):
    user = CustomUser.objects.get(id = id)
    events = user.events_set.all()
    return render(request, "users_application/user_detail.html", {'user': user})
    
def user_login(request):
    if request.method == "POST":
        email = request.POST.get('email')
        password = request.POST.get('password')
        if not CustomUser.objects.filter(email=email).exists():
            messages.error(request, 'Invalid Email')
            return redirect('login')

        user = authenticate(request, username=email, password=password)
        if user is None:
            messages.error(request, "Invalid Password")
            return redirect('login')
        else:
            login(request, user)
            return redirect('user-detail', id=user.id)

    return render(request, 'users_application/login.html')

class CustomUserAPIView(APIView):
    def get(self, request):
        users = CustomUser.objects.all()
        serializer = CustomUserSerializer(users, many=True)
        return Response(serializer.data)

class CustomUserDetailView(APIView):   
    renderer_classes = [TemplateHTMLRenderer]
    template_name = 'users_application/user_detail.html'
    
    def get_object(self, id):
        try:
            return CustomUser.objects.get(id=id)
        except CustomUser.DoesNotExist:
            raise Http404

    def get(self, request, id=None):
        if id:
            user = self.get_object(id)
            serializer = CustomUserSerializer(user)
            return Response({'user': serializer.data})
        else:
            users = CustomUser.objects.all()
            serializer = CustomUserSerializer(users, many=True)
            return Response({'users': serializer.data})

urls.py

from django.urls import path
from .views import user_detail
from .views import CustomUserAPIView, EventAPIView, CustomUserDetailView, user_login
from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path('users/', CustomUserAPIView.as_view(), name='user-list'),
    path('user/<int:id>/', CustomUserDetailView.as_view(), name='user-detail'),
    path('login/', views.user_login, name='login'),    
]

backends.py

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
            if user.check_password(password):
                return user
        except UserModel.DoesNotExist:
            return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

settings.py

from pathlib import Path

ALLOWED_HOSTS = ["*"]

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users_application',
    'rest_framework',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'REST_PROJECT.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'REST_PROJECT.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True

STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

AUTH_USER_MODEL = "users_application.CustomUser"
AUTHENTICATION_BACKENDS = ['users_application.backends.EmailBackend', 'django.contrib.auth.backends.ModelBackend']

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <form class="col-6 mx-auto card p-3 shadow-lg" method="post" enctype="multipart/form-data">
            <h1 style="text-align: center;"><span style="color: green;">USER LOGIN</span></h1>

            {% csrf_token %}
            <h3>Login</h3>
            <hr>

            {% if messages %}
            <div class="alert alert-primary" role="alert">
                {% for message in messages %}
                {{ message }}
                {% endfor %}
            </div>
            {% endif %}

            <div class="form-group">
                <label for="exampleInputEmail1">Email</label>
                <input type="email" class="form-control" name="email" id="exampleInputEmail1" aria-describedby="emailHelp"
                       placeholder="Enter email" required>
            </div>

            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password" required>
            </div>

            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
</body>
</html>

models.py

from django.contrib.auth.models import AbstractUser
from django.contrib.auth.base_user import BaseUserManager  
from django.db import models
from django.conf import settings

class CustomUserManager(BaseUserManager): 
    
    def create_superuser(self, email, 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('Superuser must have is_staff=True')
        
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True')
        
        return self.create_user(email, password, **extra_fields)

    def create_user(self, email, password, **extra_fields): 
        if not email:  
            raise ValueError('The Email must be set')  
        email = self.normalize_email(email)  

        user = self.model(email=email, **extra_fields)  
        user.set_password(password)  
        user.save()  
        return user

class CustomUser(AbstractUser):
    
    username = None
    email = models.EmailField(("email address"), unique=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []
    
    objects = CustomUserManager()

    def __str__(self):
        return self.email

class Events(models.Model):
    
    TYPES = (
        ('PRI', 'Rides'),
        ('CLN', 'Cleaning'),
        ('CPN', 'Companionship'),
        )
    
    event = models.ForeignKey(CustomUser, related_name='events', on_delete=models.CASCADE)
    date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    category = models.CharField(max_length=50, choices = TYPES)
    previous_balance = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    spent = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    add = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    remaining_balance = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    destination = models.TextField(blank=True)
排查与解决方案

核心问题定位及修复步骤

  1. 密码未哈希存储

    • 必须使用CustomUser.objects.create_user(email='xxx@xxx.com', password='yourpassword')创建用户,直接赋值user.password='xxx'会导致密码未经过哈希处理,check_password验证必然失败。
    • 若通过Django admin创建用户,需在admin.py中注册适配CustomUser的后台管理类:
      from django.contrib import admin
      from django.contrib.auth.admin import UserAdmin
      from .models import CustomUser
      
      class CustomUserAdmin(UserAdmin):
          model = CustomUser
          list_display = ('email', 'is_staff', 'is_active')
          list_filter = ('email', 'is_staff', 'is_active')
          fieldsets = (
              (None, {'fields': ('email', 'password')}),
              ('Permissions', {'fields': ('is_staff', 'is_active')}),
          )
          add_fieldsets = (
              (None, {
                  'classes': ('wide',),
                  'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
              ),
          )
          search_fields = ('email',)
          ordering = ('email',)
      
      admin.site.register(CustomUser, CustomUserAdmin)
      
  2. 修复视图导入错误

    • CustomUserDetailView中使用Http404但未导入,需添加from django.http import Http404,避免引发500错误。
  3. 添加调试日志

    • 在user_login视图中添加打印语句,确认获取的邮箱、密码是否正确,以及authenticate返回结果:
      def user_login(request):
          if request.method == "POST":
              email = request.POST.get('email')
              password = request.POST.get('password')
              print(f"Received email: {email}")
              user = authenticate(request, username=email, password=password)
              print(f"Authenticated user: {user}")
              # 原有逻辑...
      
  4. 优化认证后端逻辑

    • 在EmailBackend中添加日志,便于排查密码不匹配或用户不存在的情况:
      import logging
      logger = logging.getLogger(__name__)
      
      class EmailBackend(ModelBackend):
          def authenticate(self, request, username=None, password=None, **kwargs):
              UserModel = get_user_model()
              try:
                  user = UserModel.objects.get(email=username)
                  if user.check_password(password):
                      return user
                  else:
                      logger.warning(f"Password mismatch for user {username}")
              except UserModel.DoesNotExist:
                  logger.warning(f"User {username} does not exist")
              return None
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 13:22:00