为Django模型生成唯一哈希值

17 投票
4 回答
24362 浏览
提问于 2025-04-15 20:51

我想为每个模型使用独特的哈希值,而不是用ID。

我实现了以下这个函数,这样可以更方便地在各个地方使用。

import random,hashlib
from base64 import urlsafe_b64encode

def set_unique_random_value(model_object,field_name='hash_uuid',length=5,use_sha=True,urlencode=False):
    while 1:
        uuid_number = str(random.random())[2:]
        uuid = hashlib.sha256(uuid_number).hexdigest() if use_sha else uuid_number
        uuid = uuid[:length]
        if urlencode:
            uuid = urlsafe_b64encode(uuid)[:-1]
        hash_id_dict = {field_name:uuid}
        try:
            model_object.__class__.objects.get(**hash_id_dict)
        except model_object.__class__.DoesNotExist:
            setattr(model_object,field_name,uuid)
            return

我在寻求反馈,除了这样做还有什么其他方法吗?我该如何改进?这个方法有什么好处和坏处?

4 个回答

14

Django 1.8及以上版本自带了一个叫做UUIDField的功能。下面是一个推荐的实现方法,使用的是标准库里的uuid模块,具体可以参考官方文档

import uuid
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # other fields

如果你使用的是较老版本的Django,可以使用django-uuidfield这个包。

15

丑陋的一面:

import random

来自文档的说明:

这个模块实现了伪随机数生成器,可以用于各种分布。

如果可以的话,请使用os.urandom

返回一串适合加密使用的n个随机字节。

这是我在模型中使用它的方式:

import os
from binascii import hexlify

def _createId():
    return hexlify(os.urandom(16))

class Book(models.Model):
    id_book = models.CharField(max_length=32, primary_key=True, default=_createId)
34

我不太喜欢这一点:

uuid = uuid[:5]

在最好的情况下(也就是uuid分布得很均匀),你在生成1000个元素后,发生重复的概率会超过50%!

这就是所谓的生日问题。简单来说,证明了当元素的数量超过可能标签数量的平方根时,发生重复的概率就会超过50%。

你有0xFFFFF=10^6个标签(也就是不同的数字),所以在生成1000个值后,你就会开始遇到重复的情况。

即使你把长度扩大到-1,这里仍然会有问题:

str(random.random())[2:]

你会在生成300万后开始遇到重复(同样的计算方式适用)。

我认为你最好的选择是使用uuid,这样更有可能是唯一的,这里有个例子:

>>> import uuid
>>> uuid.uuid1().hex
'7e0e52d0386411df81ce001b631bdd31'

更新
如果你不相信数学,可以运行以下示例来看看重复的情况:

 >>> len(set(hashlib.sha256(str(i)).hexdigest()[:5] for i in range(0,2000)))
 1999 # it should obviously print 2000 if there wasn't any collision

撰写回答