Django中创建active=True的ReferralCode时,如何将同用户其他实例设为False?
实现方案
你可以通过两种常用方式实现这个需求:重写模型的save方法,或者使用Django信号。
方法一:重写模型的save方法
直接在ReferralCode模型中重写save方法,在保存当前实例前处理其他活跃邀请码的状态,同时用事务保证操作的原子性,避免并发问题。
修改后的模型代码如下:
from django.db import models, transaction from django.db.models import Q, UniqueConstraint from django.contrib.auth.models import User import random class ReferralCode(models.Model): code_str = models.CharField(max_length=64, primary_key=True, default="".join( [chr(random.randrange(48, 123)) for y in range(64)])) active = models.BooleanField(default=True) user = models.ForeignKey(User, on_delete=models.CASCADE) class Meta: constraints = [ UniqueConstraint( fields=["user", "active"], condition=Q(active=True), name="unique_active_code_for_user", ) ] def save(self, *args, **kwargs): if self.active: # 开启事务,确保修改其他邀请码和保存当前实例的操作原子化 with transaction.atomic(): # 将当前用户的其他活跃邀请码设为非活跃 ReferralCode.objects.filter(user=self.user, active=True).exclude(code_str=self.code_str).update(active=False) # 调用父类的save方法完成当前实例的保存 super().save(*args, **kwargs)
这个方法会在创建新的活跃邀请码或者将现有邀请码设为活跃时,自动把该用户其他活跃的邀请码置为非活跃。
方法二:使用Django信号
如果希望逻辑和模型解耦,可以用pre_save信号来处理,在实例保存前触发状态修改逻辑:
# 在models.py或专门的signals.py文件中添加以下代码 from django.db.models.signals import pre_save from django.dispatch import receiver from django.db import transaction from .models import ReferralCode @receiver(pre_save, sender=ReferralCode) def deactivate_old_referral_codes(sender, instance, **kwargs): if instance.active: with transaction.atomic(): # 排除当前实例,避免把自己也改成非活跃 sender.objects.filter(user=instance.user, active=True).exclude(code_str=instance.code_str).update(active=False)
如果信号放在单独的signals.py中,需要在apps.py的ready方法中导入信号,确保信号被注册:
# apps.py from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'your_app_name' def ready(self): import your_app_name.signals
注意事项
- 两种方法都使用了
transaction.atomic(),确保修改其他邀请码和保存当前实例的操作要么全部成功,要么全部失败,避免出现数据不一致的情况。 - 模型中已经存在的
UniqueConstraint会在数据库层面保证每个用户最多有一个活跃邀请码,和代码逻辑形成双重保障。
内容的提问来源于stack exchange,提问作者Alexander Mirnyy
相关产品推荐
相关产品推荐

