如何用一个对该表唯一的不同整数替换Django的主键

Saq*_*Ali 8 python mysql django hash primary-key

我有一个Django Web应用程序,它使用默认的自动递增正整数作为主键.此密钥在整个应用程序中使用,并经常插入到URL中.我不想向公众公开这个号码,以便他们可以猜测我的数据库中的用户或其他实体的数量.

这是一个经常要求,我已经向类似的矿山看到了问题和答案.大多数解决方案建议散列原始主键值.但是,这些答案都不符合我的需要.这些是我的要求:

  1. 我想将主键字段类型保持为整数.
  2. 每次读取或写入或与数据库进行比较时,我也不希望不必散列/取消该值.这看起来很浪费这样做一次会很好:当记录最初插入数据库时
  3. 散列/加密功能不需要是可逆的,因为我不需要恢复原始顺序密钥.散列值只需要是唯一的.
  4. 散列值只需对于该表是唯一的 - 不是普遍唯一的.
  5. 散列值应尽可能短.我想避免使用极长(20个以上字符)的网址

实现这一目标的最佳方法是什么?以下工作会怎样?

def hash_function(int):
    return fancy-hash-function # What function should I use??


def obfuscate_pk(sender, instance, created, **kwargs):
    if created:
        logger.info("MyClass #%s, created with created=%s: %s" % (instance.pk, created, instance))
        instance.pk = hash_function(instance.pk)
        instance.save()
        logger.info("\tNew Pk=%s" % instance.pk)

class MyClass(models.Model):
    blahblah = models.CharField(max_length=50, null=False, blank=False,)


post_save.connect(obfuscate_pk, sender=MyClass)
Run Code Online (Sandbox Code Playgroud)

e4c*_*4c5 19

想法

我会向您推荐Instragam使用的相同方法.他们的要求似乎跟随你的要求.

生成的ID应该按时间排序(例如,照片ID列表可以在不获取有关照片的更多信息的情况下进行排序)ID理想情况下应为64位(对于较小的索引,以及Redis等系统中的更好存储)系统我们应该尽可能少地推出新的"活动部件" - 我们能够用很少的工程师来扩展Instagram的很大一部分就是选择我们信任的简单,易于理解的解决方案.

他们提出了一个基于时间戳的41位系统,数据库分片为13位,自动增量部分为10位.Sincce你似乎没有使用分片.对于基于时间的copmonent,您可以只有41位,随机选择23位.如果您同时插入记录,那么确实会产生极不可能的8百万分之一的冲突机会.但在实践中,你永远不可能达到这个目的.那么一些代码怎么样:

生成ID

START_TIME = a constant that represents a unix timestamp

def make_id():
    '''
    inspired by http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram
        '''

    t = int(time.time()*1000) - START_TIME
    u = random.SystemRandom().getrandbits(23)
    id = (t << 23 ) | u

    return id


def reverse_id(id):
    t  = id >> 23
    return t + START_TIME 
Run Code Online (Sandbox Code Playgroud)

注意,START_TIME在上面的代码中是一些任意的开始时间.您可以使用time.time()*1000,获取值并将其设置为START_TIME

请注意,reverse_id我发布的方法允许您找出创建记录的时间.如果您需要跟踪这些信息,您可以这样做而无需为其添加其他字段!所以你的主键实际上是保存你的存储而不是增加它!

该模型

现在这就是你的模型的样子.

class MyClass(models.Model):
   id = models.BigIntegerField(default = fields.make_id, primary_key=True)  
Run Code Online (Sandbox Code Playgroud)

如果在django之外对数据库进行更改,则需要创建make_idsql函数的等效项

作为脚注.这有点像Mongodb 为每个对象生成它的_ID所使用的方法.


Jac*_*edd 7

您需要分开两个问题:

  1. 主键(当前是自动递增的整数)是可以在数据库级别强制执行的简单,相对可预测的唯一标识符的最佳选择.

  2. 这并不意味着您必须将其公开给您网址中的用户.

我建议在模型中添加一个新的UUID字段,并重新映射您的视图以使用它而不是PK来进行对象查找.