使用dj-rest-auth与django-allauth的用户操作异常问题求助
解决dj-rest-auth + django-allauth的两个常见问题
问题1:删除用户时触发外键约束错误
原因
django-allauth的account_emailaddress表与自定义users_user表通过外键关联,默认外键未设置级联删除,删除用户时关联的EmailAddress记录仍存在,导致数据库抛出约束错误。
解决方案
方案1:修改用户模型关联(推荐)
自定义User模型时,确保与EmailAddress的关联设置级联删除:
# models.py from django.contrib.auth.models import AbstractUser from allauth.account.models import EmailAddress class User(AbstractUser): location = models.CharField(max_length=100, blank=True) # 设置级联删除,删除用户时自动清理关联的EmailAddress email_addresses = models.ManyToManyField(EmailAddress, related_name='users', on_delete=models.CASCADE)
方案2:删除用户前清理关联记录
通过信号或视图手动删除用户对应的EmailAddress:
# 信号方式,在models.py或signals.py中 from django.db.models.signals import pre_delete from django.dispatch import receiver from allauth.account.models import EmailAddress from .models import User @receiver(pre_delete, sender=User) def cleanup_user_emails(sender, instance, **kwargs): EmailAddress.objects.filter(user=instance).delete()
问题2:注册时仅email、username、password保存,其他字段丢失
原因
RegisterSerializer存在多处逻辑错误:
get_cleaned_data返回User实例,而dj-rest-auth适配器期望接收字典格式的清理数据- 未将
first_name/last_name/location等字段赋值给创建的user实例 - 无效方法
validate_password1(字段名为password,方法名不匹配) - 错误引用
cleaned_data["password1"](实际字段是password)
解决方案
修改RegisterSerializer关键方法:
class RegisterSerializer(serializers.Serializer): password = serializers.CharField( write_only=True, required=True, validators=[validate_password], ) password2 = serializers.CharField( write_only=True, required=True, ) class Meta: model = User fields = [ "first_name", "last_name", "location", "username", "email", "password", "password2", ] extra_kwargs = { "first_name": {"required": True}, "last_name": {"required": True}, "location": {"required": False}, } def validate_username(self, username): return get_adapter().clean_username(username) def validate_email(self, email): return get_adapter().clean_email(email) # 修正方法名,匹配password字段 def validate_password(self, password): return get_adapter().clean_password(password) def validate(self, data): if data["password"] != data["password2"]: raise serializers.ValidationError( {"password": "Password fields didn't match."} ) return data # 修改为返回字典,适配适配器要求 def get_cleaned_data(self): return { "username": self.validated_data.get("username", ""), "email": self.validated_data.get("email", ""), "password": self.validated_data.get("password", ""), "first_name": self.validated_data.get("first_name", ""), "last_name": self.validated_data.get("last_name", ""), "location": self.validated_data.get("location", ""), } def save(self, request): adapter = get_adapter() user = adapter.new_user(request) self.cleaned_data = self.get_cleaned_data() # 让适配器处理所有字段保存 adapter.save_user(request, user, self, commit=True) # 正确处理密码 adapter.clean_password(self.cleaned_data["password"], user=user) user.save() return user
关键修改说明
get_cleaned_data返回字典,符合dj-rest-auth适配器的输入要求- 修正密码校验方法名,确保触发校验逻辑
- 移除
commit=False,让适配器自动保存所有字段;若需手动处理,可在commit=False后手动赋值字段再保存:adapter.save_user(request, user, self, commit=False) user.first_name = self.cleaned_data["first_name"] user.last_name = self.cleaned_data["last_name"] user.location = self.cleaned_data["location"] user.save()
内容的提问来源于stack exchange,提问作者Firas Ben said
相关产品推荐
相关产品推荐

