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

Django中Authenticate正常但Login陷入Pending状态的问题求助

问题:调用login(request, user)后应用卡住,页面持续加载
  • 可通过表单/CLI创建并认证用户,但将Patient对象传入login函数后,应用完全卡住,页面持续加载
  • 直接登录或注册后跳转均出现该问题,自定义认证后端也无效
  • CLI日志显示用户已完成认证,但login函数中的user_logged_in信号从未触发

Patient模型及管理器代码(booking app)

class PatientManager(BaseUserManager):
    """ Manager for patient profiles """
    use_in_migrations = True
     
    def _create_user(self, email, password, is_superuser, birth_date, phone_no, **extra_fields):
        """ Create a new user profile """
        if not email:
            raise ValueError('User must have an email address')
        
        email = self.normalize_email(email)
        user = self.model(email=email, password=password, is_superuser=is_superuser, birth_date=birth_date, phone_no=phone_no, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        
        return user

    def create_superuser(self, email, password, **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.')

        user = self._create_user(email=email, password=password, is_superuser=True, birth_date=date(1990, 1, 1), phone_no="0711111111")
        
        return user
class Patient(AbstractUser, PermissionsMixin):
    uuid = models.UUIDField(primary_key=True, auto_created=True, default=uuid.uuid4())
    username = None
    name = models.CharField(max_length=50, null=False)
    email = models.EmailField(max_length=255, unique=True, validators=[EmailValidator(message="Please enter a valid email address in the format"), RegexValidator(regex='^name@name.name', inverse_match=True, message="Please provide a valid email address.")])
    birth_date = models.DateField(null=False)
    gender = models.CharField(choices=GENDER_CHOICES, default="Prefer Not To Say", max_length=20)
    phone_no = models.IntegerField(null=False)
    account_type = models.CharField(max_length=10, default='PATIENT')
    kin_name = models.CharField(max_length=50, help_text="*Optional", null=True, blank=True)
    kin_contact = models.IntegerField(help_text="*Optional", null=True, blank=True)
    password = models.CharField(max_length=30, validators=[MinLengthValidator(limit_value=8, message="Please ensure the password is at least 8 characters"), RegexValidator(regex='^password', inverse_match=True, message="Please use a different password")], default="password")
    is_superuser = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    last_update = models.DateTimeField(_('last updated'), auto_now=True)

    objects = PatientManager()
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['password']
    
    def __str__(self):
        return self.email
    
    def save(self, *args, **kwargs):
        while True:
            if (datetime.today().date() - self.birth_date).days < 6390:
                raise ValidationError(f"You are a {(datetime.today().date() - self.birth_date).days} year old. Account holders should be adults who have attained at least 18 years of age.")
            if Patient.objects.filter(email = self).count() < 1:
                break
        super().save(*args, **kwargs) 

视图代码(members app views.py)

def login_user(request):
    if request.method == "POST":
        username = request.POST['email']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)
        if user:
            print(f"{username} {password} is being logged in as\n {user} {request.POST}")
            login(request, user)
            print(f"Has been authenticated {user}")
            # Redirect to a success page.
            return redirect('index')
        else:
            # Return an 'invalid login' error message.
            messages.error(request, "There Was An Error!")
            return redirect('login')
    else:
        return render(request, 'authenticate/login.html', {})

def register_user(request):
    if request.method == "POST":
        form = RegisterUserForm(request.POST)
        if form.is_valid():
            form.save()
            username = form.cleaned_data['email']
            password = form.cleaned_data['password1']
            print(f"{username} {password}")
            user = authenticate(username=username, password = password)
            print(f"{user}")
            login(request, user)
            messages.success(request, "Sign Up Completed!")
            return redirect('index')
    else:
        form = RegisterUserForm()
    return render(request, 'authenticate/register_user.html', {
        'form':form,
    })

CLI日志输出

t@a.com @octo808 is being logged in as
 t@a.com <QueryDict: {'csrfmiddlewaretoken': ['7NVGmhwyH2J2tT07jtt6SKNhOWyJl9xYUWlCtjL6lX5hJrjIvpJ7Edth8is3GAxp'], 'email': ['t@a.com'], 'password': ['@octo808']}>

问题根源

  1. Patient模型save方法存在无限循环:while True循环中,Patient.objects.filter(email = self).count() < 1的条件逻辑错误——self是Patient对象,不是邮箱字符串,且更新用户时该条件永远不满足,导致死循环。
  2. login触发用户保存操作:Django的login函数会更新用户的last_login字段(继承AbstractUser自带),调用Patient的save方法,触发死循环,导致应用卡住。

修复步骤

  1. 移除save方法中的死循环,修正年龄验证逻辑:
def save(self, *args, **kwargs):
    # 年龄验证(计算为整数年份更合理)
    age_days = (datetime.today().date() - self.birth_date).days
    if age_days < 6390:  # 18*365=6570,此处按原代码保留6390
        raise ValidationError(f"You are {age_days//365} years old. Account holders must be at least 18 years of age.")
    super().save(*args, **kwargs)
  1. 将验证逻辑移至clean方法(更规范):
def clean(self):
    super().clean()
    age_days = (datetime.today().date() - self.birth_date).days
    if age_days < 6390:
        raise ValidationError(f"You are {age_days//365} years old. Account holders must be at least 18 years of age.")
  1. 移除模型中冗余字段:继承AbstractUser后,password、is_superuser、is_active字段无需重复定义,避免覆盖默认逻辑导致认证异常。

额外优化

  • 修正PatientManager的_create_user方法:删除构造model时的password=password参数,set_password已处理密码加密,避免明文风险。

内容的提问来源于stack exchange,提问作者kurosakiaduma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:47:22