通过Django模型save方法设值时PostgreSQL触发器是否会执行?
问题解答
结论
只要Django对绑定了触发器的表发起INSERT/UPDATE操作,无论Django侧是否给created_at、updated_at赋值,PostgreSQL侧的触发器都会正常触发。
原因说明
触发器是数据库层面的执行规则,触发判断只和操作本身的属性有关:只要操作类型是INSERT/UPDATE、操作对象是绑定了触发器的表、符合行级触发的条件,不管操作来自Django ORM、原生SQL还是其他数据库客户端,都会触发执行。
你当前使用的是BEFORE INSERT OR UPDATE行级触发器,会在数据库正式写入行数据之前执行逻辑,所以会直接修改写入的值:哪怕Django已经给created_at/updated_at设了值,触发器也会用current_timestamp覆盖掉Django的赋值,最终数据库存储的是触发器生成的时间。
兼容方案
如果要避免两边逻辑冲突,可以选以下任意一种方案:
方案1:以数据库触发器的时间为准
将Django模型中的字段替换为普通的DateTimeField,关闭ORM自动赋值逻辑,完全交给触发器处理:
class TimeStampedModel(models.Model): created_at = models.DateTimeField(editable=False) updated_at = models.DateTimeField(editable=False) # 不需要自定义save方法处理赋值逻辑 class Meta: abstract = True
方案2:以Django侧的赋值为准,触发器仅做兜底
修改触发器逻辑,仅在对应字段为空的时候才赋值,避免覆盖Django已经传入的时间:
CREATE OR REPLACE FUNCTION insert_update_function() RETURNS trigger AS $BODY$ BEGIN IF TG_OP = 'INSERT' THEN IF NEW.created_at IS NULL THEN NEW.created_at := current_timestamp; END IF; IF NEW.updated_at IS NULL THEN NEW.updated_at := current_timestamp; END IF; ELSE IF NEW.updated_at IS NULL THEN NEW.updated_at := current_timestamp; END IF; END IF; RETURN NEW; END; $BODY$ LANGUAGE plpgsql VOLATILE;
该方案还有额外的兜底效果:当你使用update()、bulk_update()这类绕过save()方法的Django ORM操作时,Django不会自动更新updated_at,触发器会自动补上时间值。
内容的提问来源于stack exchange,提问作者A l w a y s S u n n y
相关产品推荐
相关产品推荐

