Django模型字段变更触发操作:OTP变更时更新OTP_expire
解决方案
在Django中,你可以通过三种方式实现「OTP字段变更时同步更新OTP_expire」的需求,无需纠结数据库触发器的字段判断问题:
方法一:重写模型的save方法(最直接)
逻辑内嵌在模型中,适合简单的单实例更新场景:
from django.utils import timezone from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin from django.db import models class Account(AbstractBaseUser, PermissionsMixin): name = models.CharField(max_length=60, null=True) username = models.CharField(max_length=30, unique=True) password = models.CharField(blank=True) OTP = models.IntegerField(null=True) OTP_expire = models.DateTimeField(null=True) def save(self, *args, **kwargs): # 仅对已存在的实例(更新操作)做判断 if self.pk is not None: original = Account.objects.get(pk=self.pk) # 对比原始OTP与当前输入的OTP值 if original.OTP != self.OTP: # 设置过期时间(示例为15分钟后,可自行调整时长) self.OTP_expire = timezone.now() + timezone.timedelta(minutes=15) # 调用父类方法完成最终保存 super().save(*args, **kwargs)
方法二:使用Django信号(解耦式实现)
如果不想修改模型的save方法,或需要在多场景复用逻辑,用pre_save信号实现解耦:
# 在你的app目录下新建signals.py文件 from django.db.models.signals import pre_save from django.dispatch import receiver from django.utils import timezone from .models import Account @receiver(pre_save, sender=Account) def update_otp_expire(sender, instance, **kwargs): if instance.pk is not None: original = sender.objects.get(pk=instance.pk) if original.OTP != instance.OTP: instance.OTP_expire = timezone.now() + timezone.timedelta(minutes=15)
注意:需要在app的apps.py中注册信号,确保项目启动时加载:
# 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
方法三:数据库触发器(支持批量更新)
如果需要处理Account.objects.filter(...).update(OTP=...)这类批量更新操作,上面两种方法不会生效(批量更新不调用模型save方法、不触发信号),此时可以用数据库触发器:
以PostgreSQL为例,创建触发器函数和触发器:
-- 创建触发器函数 CREATE OR REPLACE FUNCTION update_otp_expire() RETURNS TRIGGER AS $$ BEGIN -- 对比旧值与新值的OTP字段,判断是否变更 IF OLD.OTP IS DISTINCT FROM NEW.OTP THEN NEW.OTP_expire = NOW() + INTERVAL '15 minutes'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -- 创建触发器(替换your_app_account为实际表名,格式为app名_模型名小写) CREATE TRIGGER trigger_update_otp_expire BEFORE UPDATE ON your_app_account FOR EACH ROW EXECUTE FUNCTION update_otp_expire();
方法对比
- 重写save方法:逻辑直观,代码集中在模型内,适合常规单实例更新。
- Django信号:解耦性强,适合多场景复用或不想修改模型的情况。
- 数据库触发器:支持批量更新,但逻辑在数据库层,维护成本较高,仅在必要时使用。
内容的提问来源于stack exchange,提问作者Abhijith M R
相关产品推荐
相关产品推荐

