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

Django用户激活时自动创建免费会员报错求助

Django用户激活时自动创建免费会员的问题解决

问题描述

我有一套运行正常的Django认证系统,用户注册后会收到含激活令牌的确认邮件,功能正常。现在想添加代码实现用户点击激活链接时自动创建免费会员,但遇到两个错误:

  • 使用user_membership = UserMembership.objects.create(user=user, membership=free_membership)时,报错fromisoformat: argument must be str
  • 使用user_membership = UserMembership.objects.create(user=user, membership='Free')时,报错Cannot assign "'Free'": "UserMembership.membership" must be a "Membership" instance

相关代码

MEMBERSHIP_CHOICES = (('Premium', 'PREMIUM'),
                    ('Free', 'FREE')
)

class Membership(models.Model):
    slug = models.SlugField(null=True, blank=True)
    # name = models.CharField(max_length=100)
    description = models.TextField()
    membership_type = models.CharField(choices=MEMBERSHIP_CHOICES, default='Free',max_length=30)
    price = models.DecimalField(default=0.00, max_digits=5, decimal_places=2)

def __str__(self):
       return self.membership_type

class UserMembership(models.Model):
    user = models.OneToOneField(Customer,
                                related_name='user_membership',
                                on_delete=models.CASCADE)
    membership = models.ForeignKey(Membership, related_name='user_membership',
                                   on_delete=models.SET_NULL, null=True)
    
    start_date = models.DateTimeField(auto_now_add=True)
    end_date = models.DateTimeField(default=18000)  # Calculate based on start_date + 30 days
    
    def __str__(self):
       return self.user.username

def account_activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = Customer.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, user.DoesNotExist):
        user = None
    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()

        # Create a free membership
        free_membership = Membership.objects.get(membership_type='Free')
        
        # Convert the Membership object to string
        # free_membership_str = str(free_membership)
        print(free_membership)

        # Create a new UserMembership
        user_membership = UserMembership.objects.create(user=user, membership=free_membership)

        # user_membership = UserMembership.objects.create(user=user, membership=free_membership.membership_type)
        # user_membership = UserMembership.objects.create(user=user, membership='Free')
        # user_membership = UserMembership.objects.create(user=user, membership=self.free_membership)
        
        user_membership.save()

        # # Creating a new UserSubscription
        # user_subscription = Subscription.objects.create(user_membership=user_membership)
        # # user_subscription = Subscription()
        # # user_subscription.user_membership = user_membership
        # user_subscription.save()
        # # return user

        login(request, user)
        return redirect("homepage:homepage")
    else:
        return render(request, "account/registration/activation_invalid.html")

错误原因分析

  1. 第二个错误:UserMembership.membership是ForeignKey字段,关联到Membership模型,必须传入Membership实例,不能直接传字符串'Free',这是Django外键字段的基本要求。
  2. 第一个错误:根源在UserMembership的end_date字段,设置了default=18000,但DateTimeField需要的是datetime对象或ISO格式的字符串,整数18000无法被解析为datetime,导致抛出fromisoformat: argument must be str错误。

修复方案

1. 修正end_date字段的默认值问题

可以通过两种方式处理:

方式一:在模型重写save方法自动计算

from datetime import timedelta
from django.utils import timezone

class UserMembership(models.Model):
    user = models.OneToOneField(Customer,
                                related_name='user_membership',
                                on_delete=models.CASCADE)
    membership = models.ForeignKey(Membership, related_name='user_membership',
                                   on_delete=models.SET_NULL, null=True)
    
    start_date = models.DateTimeField(auto_now_add=True)
    end_date = models.DateTimeField(null=True)  # 移除错误的default值
    
    def __str__(self):
        return self.user.username
    
    def save(self, *args, **kwargs):
        # 若end_date未设置,自动计算为start_date+30天
        if not self.end_date:
            base_date = self.start_date if self.start_date else timezone.now()
            self.end_date = base_date + timedelta(days=30)
        super().save(*args, **kwargs)

方式二:创建UserMembership时手动指定end_date

from datetime import timedelta
from django.utils import timezone

# 在account_activate函数中创建UserMembership时:
user_membership = UserMembership.objects.create(
    user=user, 
    membership=free_membership,
    end_date=timezone.now() + timedelta(days=30)
)

2. 确保Membership实例存在

直接用get方法可能因为数据库中没有membership_type='Free'的记录而抛出异常,改用get_or_create可以自动创建缺失的免费会员记录:

# 在account_activate函数中替换原有的free_membership获取代码
free_membership, created = Membership.objects.get_or_create(
    membership_type='Free',
    defaults={
        'description': '免费会员权益',
        'price': 0.00,
        # 可以添加slug等其他字段的默认值
    }
)

3. 修正模型的__str__方法缩进

原代码中Membership类的__str__方法缩进错误,不在类内部,修正后:

class Membership(models.Model):
    slug = models.SlugField(null=True, blank=True)
    # name = models.CharField(max_length=100)
    description = models.TextField()
    membership_type = models.CharField(choices=MEMBERSHIP_CHOICES, default='Free',max_length=30)
    price = models.DecimalField(default=0.00, max_digits=5, decimal_places=2)

    def __str__(self):
        return self.membership_type

修正后的完整account_activate函数

from datetime import timedelta
from django.utils import timezone

def account_activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = Customer.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, Customer.DoesNotExist):
        user = None
    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()

        # 获取或创建免费会员实例
        free_membership, created = Membership.objects.get_or_create(
            membership_type='Free',
            defaults={
                'description': '免费会员权益',
                'price': 0.00
            }
        )
        
        # 创建UserMembership并设置end_date
        user_membership = UserMembership.objects.create(
            user=user, 
            membership=free_membership,
            end_date=timezone.now() + timedelta(days=30)
        )

        login(request, user)
        return redirect("homepage:homepage")
    else:
        return render(request, "account/registration/activation_invalid.html")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:34:54