【问题标题】:Django - short non-linear non-predictable ID in the URLDjango - URL中的短非线性不可预测ID
【发布时间】:2017-09-01 09:22:58
【问题描述】:
我知道有类似的问题(如 this、this、this 和 this),但我有特定的要求并正在寻找一种更便宜的方法来执行以下操作(在 Django 1.10.2 ):
希望不在 URL 中具有连续/可猜测的整数 ID,并且理想情况下满足以下要求:
- 避免使用 UUID,因为这会使 URL 非常长。
- 避免使用自定义主键。如果模型有 ManyToManyFields,它似乎不能很好地工作。在尝试时受到至少三个错误的影响(#25012、#24030 和 #22997),包括弄乱迁移并不得不删除整个数据库并重新创建迁移(嗯,还有很多很好的学习)
- 尽可能避免检查冲突(因此避免对每个插入进行数据库查找)
- 不要只通过 slug 进行查找,因为它的性能不如仅查找整数 id。
- 不要太在意加密 id - 只是不希望它加密
是一个可见的连续整数。
注意:从长远来看,该应用可能有 500 万条左右的记录。
【问题讨论】:
标签:
python
django
django-models
django-views
【解决方案1】:
在研究了很多关于 SO、博客等的选项后,我最终做了以下事情:
-
仅为 URL 将 id 编码为 base32 并在 urls.py 中将其解码(使用 Django 的 util functions 的编辑版本编码为 base 36,因为我需要大写字母而不是小写字母)。
- 不在任何地方存储编码的ID。每次即时进行编码和解码。
- 保持默认 ID 不变并将其用作主键。
(好的hints、posts 尤其是this 评论帮助很大)
此解决方案有助于实现的目标:
- 绝对不修改模型或 post_save 信号。
- 不需要碰撞检查。避免向数据库发出一个额外请求。
- 仍然会在默认 ID 上进行查找,这很快。此外,每个插入的模型都没有双重保存()请求。
- 短而甜的编码ID(字符数随着记录数的增加而增加,但仍然不是很长)
它无助于实现什么/任何缺点:
- 加密 - ID 已编码但未加密,因此用户可以
仍然能够找出获取 id 的模式(但我不知道
非常关心它,如上所述)。
- 每个 URL 构造/请求的编码和解码开销很小,但这可能比碰撞检查和/或对模型对象多次调用 save() 以进行插入要好。
作为参考,看起来有多种方法可以生成我在此过程中发现的随机 ID(如 Django 的 get_random_string、Python 的随机、Django 的 UUIDField 等)以及许多编码当前 ID 的方法(基数 36 ,base 62,XORing,等等)。
编码后的 ID 也可以存储为另一个(索引)字段并每次查找(如 here),但取决于 Web 应用程序的性能参数(因为查找 varchar id 的性能不如查找整数 id )。此标识符字段可以从覆盖模型的 save() 函数中保存,也可以使用 post_save() 信号(请参阅here)(虽然这两种方法都需要为每次插入调用两次 save() 函数)。
全心全意对上述方法进行优化。我爱 SO 和社区。每次在这里都可以学到很多东西。
更新: 写完这篇文章一年多后,我发现了这个名为 hashids 的很棒的库,它几乎完成了同样的事情!它支持多种语言,包括Python。