Django中关联User模型仅用邮箱密码注册登录报错的解决方法
Got it, let's break down how to solve this problem. The root issue is that Django's default User model requires a username field, but you want to build auth flows that only use email and password. Here are two solid solutions, with the first being the best long-term approach:
Django officially recommends defining a custom user model at the start of your project—this lets you fully replace the default auth logic and use email as the primary identifier, no username required. Here's how to set it up:
Step 1: Create the Custom User Model
In your app's models.py, add this code:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.db import models class CustomUserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('You must provide an email address') # Normalize email (lowercase domain part) email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): # Set admin-specific flags extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superusers must have is_staff=True') if extra_fields.get('is_superuser') is not True: raise ValueError('Superusers must have is_superuser=True') return self.create_user(email, password, **extra_fields) class CustomUser(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) objects = CustomUserManager() # Use email as the login identifier instead of username USERNAME_FIELD = 'email' # No extra required fields beyond email and password REQUIRED_FIELDS = [] def __str__(self): return self.email
Step 2: Update Project Settings
In your settings.py, tell Django to use your custom user model:
AUTH_USER_MODEL = 'your_app_name.CustomUser'
Important: This must be set before running your first
migratecommand. If you've already migrated, you'll need to reset your database or create a migration to switch models (which is more complex).
Step 3: Build Auth Views & Forms
You can now create registration and login views that only ask for email and password. For example, a registration form:
from django import forms from .models import CustomUser class CustomUserCreationForm(forms.ModelForm): password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput) class Meta: model = CustomUser fields = ('email',) def clean_password2(self): # Verify password match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): # Save the hashed password user = super().save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user
And a registration view:
from django.contrib.auth import login from django.shortcuts import render, redirect from .forms import CustomUserCreationForm def register(request): if request.method == 'POST': form = CustomUserCreationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) return redirect('home') else: form = CustomUserCreationForm() return render(request, 'auth/register.html', {'form': form})
If you can't switch to a custom user model right now, you can auto-generate a username when creating users (using the email to avoid manual input). This is a quick fix but not ideal for long-term maintenance:
Example Registration View
from django.contrib.auth.models import User from django.contrib.auth import authenticate, login import hashlib import random def register(request): if request.method == 'POST': email = request.POST.get('email') password = request.POST.get('password') # Generate a unique username from the email (e.g., MD5 hash truncated to 15 chars) username = hashlib.md5(email.encode()).hexdigest()[:15] # Handle username collisions while User.objects.filter(username=username).exists(): username += str(random.randint(0,9)) # Create the user with auto-generated username user = User.objects.create_user(username=username, email=email, password=password) # Link to your OneToOne model here if needed # YourProfileModel.objects.create(user=user) # Log the user in authenticated_user = authenticate(request, username=user.username, password=password) if authenticated_user: login(request, authenticated_user) return redirect('home') return render(request, 'auth/register.html')
Adjust Login to Use Email
Since users will log in with email, modify your login view to look up the user by email:
def login_view(request): if request.method == 'POST': email = request.POST.get('email') password = request.POST.get('password') user = User.objects.filter(email=email).first() if user and authenticate(request, username=user.username, password=password): login(request, user) return redirect('home') # Add error handling for invalid credentials return render(request, 'auth/login.html')
Final Note
Stick with the custom user model if you can—it's the cleanest, most scalable solution. The auto-generated username workaround works for quick tests but can cause headaches down the line (like username collisions or limited flexibility for adding user fields).
内容的提问来源于stack exchange,提问作者lionheart

