Генерация уникальных хешей для моделей Django - PullRequest
17 голосов
/ 26 марта 2010

Я хочу использовать уникальные хеши для каждой модели, а не идентификаторы.

Я реализовал следующую функцию, чтобы легко использовать ее по всем направлениям.

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 ]

32 голосов
/ 26 марта 2010

Мне не нравится этот бит:

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
14 голосов
/ 26 марта 2010

Безобразный:

случайный импорт

Из документации:

В этом модуле реализованы псевдослучайные генераторы чисел для различных распределений.

Если что, пожалуйста, используйте 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)
7 голосов
/ 21 апреля 2016

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 .

7 голосов
/ 26 марта 2010

Используйте поддержку UUID вашего движка базы данных вместо создания собственного хэша. Почти все, кроме SQLite, поддерживает их, поэтому нет особых причин не использовать их.

...