自定义Django User模型后无法登录Admin面板的问题排查与解决
自定义Django User模型后超级用户无法登录Admin面板的问题排查
我自定义了Django的User模型,但创建的超级用户无法登录Admin面板,以下是我的代码和环境信息,求排查原因及解决方法:
环境信息
- Django 3.2
- Python 3.9
- PostgreSQL
- Docker(开发环境)
相关代码
users/models.py(原文件名为users/user.py,修正命名规范)
from django.contrib.auth.models import AbstractUser from django.db import models from ..managers.user import UserManager class User(AbstractUser): objects = UserManager()
managers/user.py
from django.contrib.auth.models import BaseUserManager class UserManager(BaseUserManager): def create_superuser(self, username, email=None, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self.create_user(username, email, password, **extra_fields) def create_user(self, email, username=None, password=None, **extra_fields): if not username: raise ValueError('The username field must set') email = self.normalize_email(email) user = self.model(username=username, email=email, **extra_fields) user.set_password(password) user.save(using=self.db) return user
users/serializers.py(原文件名为user/serializer.py,修正拼写)
from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from rest_framework import serializers User = get_user_model() class UserReadSerializer(serializers.ModelSerializer): class Meta: model = User fields = ('id', 'username') class UserWriteSerializer(serializers.ModelSerializer): password = serializers.CharField(validators=[validate_password], write_only=True) class Meta: model = User fields = ('password', 'username') def create(self, validated_data): password = validated_data.pop('password') username = validated_data.get('username') user = User.objects.create_user(username=username, password=password) return user
settings/base.py
from typing import List DEBUG = True # 生产环境需设为False SECRET_KEY = NotImplemented ALLOWED_HOSTS: List[str] = ['*'] CORS_ALLOW_ALL_ORIGINS = True CSRF_TRUSTED_ORIGINS: List[str] = [] INTERNAL_IPS = [ '127.0.0.1', ] APPEND_SLASH = False # 应用定义 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # 第三方应用 'corsheaders', 'rest_framework', 'rest_framework_simplejwt', 'django_filters', 'storages', # 自定义应用 'cores.authentication.apps.AuthenticationConfig', 'cores.users.apps.UsersConfig', 'cores.cards.apps.CardsConfig', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'cores.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', ], }, }, ] ASGI_APPLICATION = 'cores.project.asgi.application' WSGI_APPLICATION = 'cores.project.wsgi.application' # 数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'trinion', 'USER': 'trinion', 'PASSWORD': 'trinion', 'HOST': 'localhost', 'PORT': '5432', 'ATOMIC_REQUESTS': True, 'CONN_MAX_AGE': 600, } } # 密码验证器 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', }, ] AUTH_USER_MODEL = 'users.User' LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # 媒体文件配置 MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media' # 静态文件配置 STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' DATA_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10MB FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10MB DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
settings/rest_framework.py
from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(days=365), 'REFRESH_TOKEN_LIFETIME': timedelta(days=30), 'UPDATE_LAST_LOGIN': True, } REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',), 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter', ], 'DEFAULT_PARSER_CLASSES': ('rest_framework.parsers.JSONParser',), 'TEST_REQUEST_DEFAULT_FORMAT': 'json', }
问题排查与解决方法
1. 核心问题:UserManager方法参数顺序不匹配
你的create_superuser调用create_user时,传参顺序和create_user的定义完全颠倒:
create_superuser传参顺序是username, email, password- 但
create_user的参数定义是email, username=None, password=None
这会导致:
- 你创建超级用户时输入的
username被存到了email字段 username字段要么是None(创建时没填邮箱),要么是你输入的邮箱地址
解决方法:修改create_user的参数顺序,匹配调用逻辑:
def create_user(self, username, email=None, password=None, **extra_fields): if not username: raise ValueError('The username field must set') # 仅当email不为空时做归一化处理 if email: email = self.normalize_email(email) # 显式设置is_active,避免意外被覆盖 extra_fields.setdefault('is_active', True) user = self.model(username=username, email=email, **extra_fields) user.set_password(password) user.save(using=self.db) return user
2. 修复后重新创建超级用户
先删除已创建的错误超级用户:
python manage.py shell
from django.contrib.auth import get_user_model User = get_user_model() User.objects.filter(is_superuser=True).delete() exit()
然后重新创建超级用户:
python manage.py createsuperuser
3. 其他可能的问题排查
- Admin注册问题:确保在
users/admin.py中正确注册自定义User模型:from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import User admin.site.register(User, UserAdmin) - CSRF验证问题:Docker环境下,添加访问地址到
CSRF_TRUSTED_ORIGINS:# settings/base.py CSRF_TRUSTED_ORIGINS = ['http://localhost:8000'] # 替换成你的实际访问地址
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

