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)
排查与解决方案
核心问题定位及修复步骤
密码未哈希存储
- 必须使用
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)
- 必须使用
修复视图导入错误
CustomUserDetailView中使用Http404但未导入,需添加from django.http import Http404,避免引发500错误。
添加调试日志
- 在
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}") # 原有逻辑...
- 在
优化认证后端逻辑
- 在
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
相关产品推荐
相关产品推荐

