我想为每个模型使用唯一的哈希,而不是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
我正在寻求反馈,我怎么能这样做?我怎样才能改进它?什么是坏事和丑陋的呢?
答案 0 :(得分:32)
我不喜欢这一点:
uuid = uuid[:5]
在最好的情况下(uuid是均匀分布的),你会在1k元素之后发生概率大于0.5的碰撞!
这是因为birthday problem。简而言之,当元素数量大于可能标签数量的平方根时,证明碰撞概率超过0.5。
您有0xFFFFF = 10 ^ 6个标签(不同的数字),因此在生成1000个值后,您将开始发生碰撞。
即使你将长度增加到-1,你仍然有问题:
str(random.random())[2:]
你将在3 * 10 ^ 6之后开始碰撞(接着进行相同的计算)。
我认为你最好的选择是使用更有可能是唯一的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
答案 1 :(得分:14)
丑陋的:
导入随机
此模块为各种发行版实现伪随机数字生成器。
如果有的话,请使用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)
答案 2 :(得分:7)
使用数据库引擎的UUID支持,而不是编写自己的哈希。除了SQLite之外,几乎所有东西都支持它们,因此没有理由不使用它们。
答案 3 :(得分:7)
Django 1.8+有一个内置的UUIDField
。以下是使用标准库uuid
模块的建议实现,来自the docs:
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包。