Django pre_save信号 - 异常会导致事务失败吗?

6 投票
2 回答
3385 浏览
提问于 2025-04-17 20:59

我想在创建用户之前做一些自定义的操作,所以我想到了使用 pre_save 信号来实现。如果其中某个操作出错了,就应该停止这个过程,不再创建用户。

  1. 这样做可以吗?如果这个步骤出错了,会不会中止保存用户(这是我想要的效果)?我觉得应该可以,但找不到相关的文档。

  2. 获取未来用户的 ID 最好的方法是什么?我理解在 pre-save 阶段用户的 ID 还不存在,但我需要它作为一些额外操作的输入。

谢谢

2 个回答

3

这里有两个部分的回答:

  1. 首先,在pre_save()信号中抛出一个异常会停止调用save()方法。比如,我在我的pre_save()里这样做:

    if SOME_TEST:
        raise exceptions.ParseError(detail="I reject your data. Here is why.")
    

    注意:这里使用的是DRF的异常处理,但我相信你知道怎么抛出你想要的任何异常。

  2. 你可以使用post_save()信号在save()方法之后获取用户的ID。不过,如果这样不适合你,下面是我最初的解释,介绍一些在pre_save()中实现你想要的功能的方法:

在pre_save中,你可以访问User.objects.all()(当然前提是你导入了User模型)。如果你使用的是标准的User模型,那么User.id的值是自动递增的。所以如果你需要知道新用户的ID将会是什么,只需获取数据库中当前最高的User.id(也就是最后创建的用户的ID)。这里有一个简单的查询来做到这一点:

last_user = User.objects.latest('id')

然后,当然,last_user.id会给你最后创建的用户的ID值,如果你在这个值上加一,你就得到了下一个用户的ID。当然,如果你的网站访问量很大,可能会出现同时创建用户的问题。风险在于,这个值可能会被两个(或更多)用户创建尝试获取,导致其中一个(或多个)结果出错。

当然,你也可以设置另一个字段为primary_key=True,这样就会替代模型中的id字段。然后你可以设计任何你能想到的索引机制!你可以使用某个独特特征的哈希值。例如:User.username有唯一约束,你可以将其哈希或十六进制编码为一个数字ID,以预先确定User.id。或者你可以保留id字段,并在pre_save中通过给obj.id赋值来手动设置它。这个值可以是你想要的任何东西,甚至是一个哈希值!

3

来自文档

在你调用save()之前,无法知道ID的值是什么,因为这个值是由你的数据库决定的,而不是由Django决定的。

所以,如果你的保存前处理需要user.id,那恐怕是做不到的。

撰写回答