1

您如何生成一个唯一的随机数以设置为 django 中对象的主键?

编辑 - 随机数不必是主键,但它需要对每个对象都是唯一的,以便我可以通过该数字引用/获取/调用对象。

4

3 回答 3

5

任何随机生成的数字唯一性都受到生成随机数的空间大小的限制。UUID/GUIDS 的长度为 128 位,因此发生冲突的可能性很小。然而,除了第 4 版之外的0x4所有UUID都不是完全随机的(即使是那些,一个半字节固定在尽管他为 Microsoft 工作,而且他的大部分专栏都与 Microsoft 相关,但这篇文章适用于使用 UUID/GUID 的任何内容)。

因此,我们不要为此考虑 UUID。首先需要确定的是您需要多大的空间,然后可以确定适合您需求的编码方案。这在很大程度上取决于您要参考的项目数量。由于生日问题,两个随机生成的数字之间发生冲突的可能性非常低,维基上的生日攻击页面提供了一个近似值,即:

在此处输入图像描述

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 个字符等。Base 64 值区分大小写,如果要将一个值放入 URL 必须小心(+ 和 / 都是 base 64 编码的一部分,并且可以替换为 . 和 _ 例如,用于 URL 编码)。这将使它们更难键入,但对于较大的值会更短(base64 为 11 个字符,而 64 位值的十六进制为 16 个字符,并且这种节省会增加)。

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

于 2012-08-02T05:32:57.970 回答
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'

长 uuid 旨在避免冲突并保证高度的唯一性。如果您需要更短的 id,这将取决于您打算如何使用它。如果它需要在所有模型中唯一,因为您将其用作根 url,/<slug>/那么它将增加您在分配之前查询数据库检查唯一性的需要。

您可能想查看使用slugfield和这样的片段,它在模型保存时设置唯一的 slug 值。

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

于 2012-08-02T04:21:17.710 回答
2

以下是使用 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).exists()
            self.id = id
        super(Account, self).save()
于 2017-11-16T06:42:39.550 回答