如何安全地转发Django中的主键序列?

时间:2012-02-16 08:34:59

标签: django postgresql sequence

将Django与PostgreSQL(8.x)后端一起使用,我有一个模型,我需要跳过一块id,例如在给出49999之后,我希望下一个id为70000而不是50000(因为该块是为另一个源保留的,其中实例是用id明确添加的 - 我知道这不是一个很棒的设计,但它是我必须使用的。)< / p>

这样做的最正确/最安全的地方是什么?

我知道我可以用

设置序列
SELECT SETVAL(
    (SELECT pg_get_serial_sequence('myapp_mymodel', 'id')),
    70000,
    false
);

但Django什么时候从序列中拉出一个数字? 我是否覆盖MyModel.save(),调用它的超级,然后抓住一个光标并检查

SELECT currval(
    (SELECT pg_get_serial_sequence('myapp_mymodel', 'id'))
);

我相信django可能会推进一个序列,即使保存模型失败了,所以我想确保无论什么时候它都会触及它前进的数字 - 还有比save()更好的地方吗?

P.S。:即使这是可行的方法 - 我真的可以像这样找出save()会话的currval吗?如果我抓住一个连接和光标,并执行第二个SQL语句,我不会在另一个会话中,因此没有得到一个currval?

感谢您的任何指示。

编辑:我觉得这必须在数据库级别完成(并发问题)并发布相应的PostgreSQL问题 - How can I forward a primary key sequence in PostgreSQL safely?

2 个答案:

答案 0 :(得分:0)

由于我还没有找到一种“自动化”方式,我正在考虑以下解决方法 - 这对我的特殊情况是可行的:

  1. 使用MAXVALUE 49999 NO CYCLE
  2. 设置序列
  3. 当达到49999时,下一个save()将遇到postgres错误
  4. 抓住该异常并重新加注为表单错误“您的数字已用完,请重置为下一个区块,然后重试”
  5. 提供一个用户可以激活下一个块的视图,即执行“ALTER SEQUENCE my_seq RESTART WITH 70000 MAXVALUE 89999”
  6. 我很难在捕获异常时自动重启:

    try:
        instance.save()
    except RunOutOfIdsException:
        restart_id_sequence()
        instance.save()
    

    因为我担心两个并发的save()用完了id会导致两次单独的重启,并且后续违反了唯一约束。 (与原始问题基本相同的概念)

答案 1 :(得分:0)

我的下一个想法是不使用主键的序列,而是总是从一个单独的计数器表中明确指定id,我在使用其最新数字之前检查/更新 - 应该是避免并发问题。唯一的问题是虽然我只有一个地方可以添加模型实例,但django或第三方应用程序的其他部分仍然可能依赖于隐式ID,我不想破坏它。

但同样的机制碰巧很容易在postgres级别实现 - 我相信这是解决方案:

  • 不要将SERIAL用作主键,请使用DEFAULT my_next_id()
  • 遵循与“单级无间隙序列”相同的逻辑 - http://www.varlena.com/GeneralBits/130.php - my_next_id()执行更新,然后执行选择
  • 不是仅增加1,而是检查边界是否交叉,如果是,则进一步增加