Создание уникальных хэшей для моделей django
Я хочу использовать уникальные хэши для каждой модели, а не идентификаторы.
Я использовал следующую функцию, чтобы легко использовать ее по всем разделам.
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
Я ищу отзыв, как еще я могу это сделать? Как я могу улучшить его? Что плохого и уродливого в этом отношении?
Ответы
Ответ 1
Мне не нравится этот бит:
uuid = uuid[:5]
В наилучшем сценарии (uuid равномерно распределены) вы получите столкновение с вероятностью более 0,5 после 1k элементов!
Это связано с днем рождения. Вкратце доказано, что вероятность столкновения превышает 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
Ответ 2
Уродливый:
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)
Ответ 3
Используйте поддержку UUID в базе данных, вместо того, чтобы создавать свой собственный хеш. Почти все, что находится за пределами SQLite, поддерживает их, поэтому нет причин не использовать их.
Ответ 4
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.