2010-03-25 11 views
17

Quiero utilizar hashes únicos para cada modelo en lugar de identificadores.Generar hashes únicos para los modelos de django

Implementé la siguiente función para usarla fácilmente en todos los ámbitos.

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 

Estoy buscando comentarios, ¿cómo podría hacerlo? ¿Cómo puedo mejorarlo? ¿Qué es bueno, malo y feo?

+0

¿Podría aclarar por favor: ¿Necesita hashes aleatorios o hashes únicos a través del tiempo y el espacio? Lo pregunto porque a menudo los usuarios solo quieren lo primero, pero usan los términos "único" y "aleatorio" de manera intercambiable. – nikola

+0

¿Podría publicar una actualización de cómo lo arregló? Estoy buscando una solución. – Thomas

+0

Es una locura generar una identificación única. Si haces uuid simplemente generas un uuid y no cambias la longitud ni nada. Configure su modelo para usar ese campo como la clave principal. Simplemente genere un uuid en guardar, ni siquiera se preocupe por las colisiones. – dalore

Respuesta

7

Utilice el soporte UUID de su motor de base de datos en lugar de crear su propio hash. Casi todo lo que está más allá de SQLite los admite, por lo que hay pocas razones para no usarlos.

+9

Sería bueno ver algunos ejemplos sobre cómo hacer eso con el ORM de Django. –

+0

bueno, no es cosa de "django". En postgresql tienes este http://www.postgresql.org/docs/8.3/static/datatype-uuid.html. Creo que en otras bases de datos, tienen algo similar –

30

No me gusta este bit:

uuid = uuid[:5] 

En el mejor de los casos (UUID se distribuyen de manera uniforme) obtendrá una colisión con una probabilidad superior a 0,5 después de 1k de los elementos!

Es a causa de birthday problem. En un resumen, se demuestra que la probabilidad de colisión excede de 0.5 cuando el número de elementos es mayor que la raíz cuadrada a partir del número de etiquetas posibles.

Tiene 0xFFFFF = 10^6 etiquetas (números diferentes) así que después de 1000 de valores generados comenzará a tener colisiones.

aunque amplíe longitud a -1 tiene todavía un problema aquí:

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

Va a empezar a tener colisiones después de 3 * 10^6 (el mismo cálculo siguiente).

creo que la mejor opción es utilizar UUID que es más probable que sea único, aquí es un ejemplo

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

actualización Si usted no confía en matemáticas basta con ejecutar el siguiente ejemplo para ver la colisión:

>>> 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 
+4

El problema del cumpleaños en realidad se aplica a la generación de números aleatorios. Sin embargo, el paquete uuid de Python no se refiere a la generación de números aleatorios en específico. En realidad, uuid1() de su ejemplo no está ni remotamente al azar como en la seguridad criptográfica. Simplemente señalando esto en caso de que alguien tenga la idea de equiparar el paquete uuid de Python con la generación de números aleatorios. – nikola

14

Lo feo:

importación azar

From the documentation:

Este módulo implementa generadores de números pseudo-aleatorios para diversas distribuciones.

En todo caso, por favor utilice os.urandom

devolver una cadena de bytes n aleatorios adecuados para su uso criptográfico.

Esta es la forma en que lo uso en mis modelos:

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) 
+0

Una de las cosas que se debe tener en cuenta es que urandom es mucho más lento que pseudoaleatorio, por lo que si no lo necesita por razones criptográficas, puede que no valga la pena usarlo. En mi Mac OSX es ** 21 veces ** más lento. Considere:. >>> timeit.Timer ('importación azar; random.random()') timeit (100000) ,1538231372833252 >>> timeit.Timer ('os importación; os.urandom (2)') .timeit (100000) 3.1858959197998047 –

+0

Acabo de comprobar que uuid es aún más lento :) –

+0

¡Esto es peligroso! Si tiene una colisión clave, su nuevo registro sobrescribirá silenciosamente su registro existente. – sherbang

5

Django 1.8+ tiene incorporado UUIDField. Aquí está la aplicación sugerida, utilizando el módulo de la biblioteca estándar uuid, desde 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 

Para versiones anteriores de Django puede utilizar el paquete django-uuidfield.

Cuestiones relacionadas