如何在Django中生成随机数作为对象的id /主键

时间:2012-08-02 03:56:30

标签: django random primary-key

如何在django中生成一个唯一的随机数作为对象的主键?

编辑 - 随机数不一定是主键,但它必须对每个对象都是唯一的,这样我才能用该数字引用/获取/调用该对象。

3 个答案:

答案 0 :(得分:5)

任何随机生成的数字唯一性都受限于生成随机数的空间有多大。 UUID / GUIDS长128位,因此碰撞的可能性很小。但是,除了版本4之外的所有UUID都不是完全随机的(即使有那些,一个半字节固定在0x4),因此,UUID的一个子部分不能被认为是唯一的(参见Raymond Chen's post on GUIDs for更多信息,还要注意虽然他在微软工作,并且他的大多数专栏都与微软有关,但这个专栏适用于使用UUID / GUID的任何内容。)

所以,我们不要为此考虑UUID。首先需要决定的是你需要多大的空间,然后可以根据你的需要决定编码方案。这在很大程度上取决于您想要引用的项目数。由于Birthday Problem,两个随机生成的数字之间碰撞的可能性非常低,而Wiki上的Birthday Attack页面提供了近似值,即:

enter image description here

H是可能值的数量,Q(H)是我们在遇到碰撞之前可以生成的项目数。我将假设冲突是不合需要的,因为要检查冲突,很可能你必须命中数据库以查看生成的数字是否存在,如果存在,则创建另一个并再次检查它。随着数据库中的项目越来越多,此过程将花费更长时间。当然,你仍然想要检查碰撞,但你应该多次检查的可能性应该非常低。

所以,让我们从32位值开始。从上面的公式中,您将有大约82,000个项目生成,然后才能发生碰撞。如果你只期望几千或几万,这可能是一个可以接受的位数。对于其他一些值,这里是为多个位生成的项目数量:

16 bits: 320
24 bits: 5100
32 bits: 82,000
40 bits: 1.3*16^6
48 bits: 2.1*10^7
64 bits: 5.4*10^9

我会认为这些计数是您期望在表中获得的最大值。如果它与安全相关,我会选择比你需要的范围大得多的范围(外部可见的用户ID,你不希望别人猜测其他人,最多几百个用户?48位是我最不习惯的有)

在特定说明中,我会使用random.getrandbits()为非安全相关项生成这些数字,对于那些,我会使用ssl.RAND_bytes()代替。

现在,问题的另一部分:将这些随机位编码为可打印的内容。最基本的是十六进制编码,我们是0-9A-F,长度将是你生成的位数除以4(32位标识符将是8个字符,40位10,等等)。这将不区分大小写并且最容易输入。

另一种选择是base-64编码。这将导致输出(1/6)* n(向上舍入)字符(其中n是位数)。因此对于32位,6个字符,40位,7个字符等。基本64值区分大小写,如果要将一个值放入URL中,则必须小心(+和/都是base 64编码的一部分,并且可以替换为。和_例如,用于URL编码)。这将使它们更难打字,但对于较大的值则更短(base64为11个字符,64位为16个十六进制值,此节省增加)。

虽然这并没有直接回答你的问题(虽然我想你知道如何在数据库中为存储分配值,但请记住,这些值应该存储在数据库中的字符串编码形式中,或者作为BLOB,因为您的数据库可能会将这些值中的某些值视为已签名,并导致不良内容),它应该为您提供您需要知道的内容,以便为您的应用程序找出正确的组合。

答案 1 :(得分:2)

也许只是生成一个uuid?

>>> from uuid import uuid4
>>> uid = uuid4()
>>> uid
UUID('88016297-726a-4a42-a5d3-7c1047e27cac')
>>> uid.int
180782199398610579001229174541650132140L
>>> uid.hex
'88016297726a4a42a5d37c1047e27cac'
>>> uid.bytes
'\x88\x01b\x97rjJB\xa5\xd3|\x10G\xe2|\xac'

Long uuid旨在避免碰撞,并在很大程度上保证唯一性。如果您需要更短的ID,这将取决于您打算如何使用它。如果它需要在所有模型中都是唯一的,因为您将它用作根URL /<slug>/,那么它将增加您在分配之前查询数据库检查唯一性的需要。

您可能希望使用slugfieldsnippet like this来设置模型保存中的唯一slug值。

简而言之......长UUID ==即时值。较短的slug值==查询循环以确认唯一性。

答案 2 :(得分:0)

以下是使用Django模型的方法:

from django.db import models
from django.contrib.auth.models import User
from random import randint


class Account(models.Model):
    id = models.BigAutoField(primary_key=True)
    user = models.OneToOneField(User, related_name='account',on_delete=models.CASCADE)

    def save(self):
        if not self.id:
            is_unique = False
            while not is_unique:
                id = randint(1000000000000000000, 1999999999999999999) # 19 digits: 1, random 18 digits
                is_unique = (Account.objects.filter(id=id).count() == 0)
            self.id = id
        super(Account, self).save()