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

使用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存在多处逻辑错误:

  1. get_cleaned_data返回User实例,而dj-rest-auth适配器期望接收字典格式的清理数据
  2. 未将first_name/last_name/location等字段赋值给创建的user实例
  3. 无效方法validate_password1(字段名为password,方法名不匹配)
  4. 错误引用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:14:52