python mysql django hash primary-key

python - Cómo reemplazar la clave principal de Django con un número entero diferente que es único para esa tabla



mysql hash (4)

Tengo una aplicación web de Django que utiliza los enteros positivos predeterminados con incremento automático como clave principal. Esta clave se utiliza en toda la aplicación y se inserta con frecuencia en la URL. No quiero exponer este número al público para que puedan adivinar el número de usuarios u otras entidades en mi Base de datos.

Este es un requisito frecuente y he visto preguntas similares a las mías con respuestas. La mayoría de las soluciones recomiendan cambiar el valor de la clave primaria original. Sin embargo, ninguna de esas respuestas se ajusta exactamente a mi necesidad. Estos son mis requisitos:

  1. Me gustaría mantener el tipo de campo Clave primaria como Entero.
  2. También preferiría no tener que hacer hash / unhash este valor cada vez que se lee o se escribe o se compara con la base de datos. Parece un desperdicio Sería bueno hacerlo solo una vez: cuando el registro se inserta inicialmente en la Base de datos
  3. La función de cifrado / hashing no necesita ser reversible ya que no necesito recuperar la clave secuencial original. El valor hash solo necesita ser único.
  4. El valor hash debe ser único SOLAMENTE para esa tabla, no universalmente único.
  5. El valor hash debe ser lo más corto posible. Me gustaría evitar URLs extremadamente largas (más de 20 caracteres)

¿Cuál es la mejor manera de lograr esto? ¿Funcionaría lo siguiente?

def hash_function(int): return fancy-hash-function # What function should I use?? def obfuscate_pk(sender, instance, created, **kwargs): if created: logger.info("MyClass #%s, created with created=%s: %s" % (instance.pk, created, instance)) instance.pk = hash_function(instance.pk) instance.save() logger.info("/tNew Pk=%s" % instance.pk) class MyClass(models.Model): blahblah = models.CharField(max_length=50, null=False, blank=False,) post_save.connect(obfuscate_pk, sender=MyClass)


La idea

Le recomendaría el mismo enfoque que utiliza Instragam . Sus requisitos parecen seguir de cerca los suyos.

Las ID generadas deben poder clasificarse por tiempo (por lo que una lista de ID de fotos, por ejemplo, podría clasificarse sin obtener más información sobre las fotos) Las ID deberían ser idealmente de 64 bits (para índices más pequeños y mejor almacenamiento en sistemas como Redis). El sistema debería introducir la menor cantidad posible de "piezas móviles" nuevas; una gran parte de cómo hemos podido escalar Instagram con muy pocos ingenieros es eligiendo soluciones simples y fáciles de entender en las que confiamos.

Se les ocurrió un sistema que tiene 41 bits basados ​​en la marca de tiempo, 13 del fragmento de la base de datos y 10 para una porción de incremento automático. Como parece que no estás usando fragmentos. Solo puede tener 41 bits para un copmonente basado en el tiempo y 23 bits elegidos al azar. Eso produce una probabilidad extremadamente improbable de 1 en 8.3 millones de tener un conflicto si inserta registros al mismo tiempo. Pero en la práctica, nunca es probable que golpees esto. Bien, ¿qué tal un código?

Generando ID

START_TIME = a constant that represents a unix timestamp def make_id(): '''''' inspired by http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram '''''' t = int(time.time()*1000) - START_TIME u = random.SystemRandom().getrandbits(23) id = (t << 23 ) | u return id def reverse_id(id): t = id >> 23 return t + START_TIME

Tenga en cuenta que START_TIME en el código anterior es una hora de inicio arbitraria. Puede usar time.time () * 1000, obtener el valor y establecerlo como START_TIME

Tenga en cuenta que el método reverse_id que reverse_id le permite saber en qué momento se creó el registro. Si necesita realizar un seguimiento de esa información, puede hacerlo sin tener que agregar otro campo. ¡Entonces su clave principal es realmente guardar su almacenamiento en lugar de aumentarlo!

El modelo

Ahora así es como se vería su modelo.

class MyClass(models.Model): id = models.BigIntegerField(default = fields.make_id, primary_key=True)

Si realiza cambios en su base de datos fuera de django, necesitaría crear el equivalente de make_id como una función sql

Como una nota al pie. Esto es algo así como el enfoque utilizado por Mongodb para generar su _ID para cada objeto.


Guarde el AUTO_INCREMENT , pero páselo de forma semisecreta: en una cookie. Se necesita un poco de codificación para establecer la cookie, configurarla y leerla. Pero las cookies están ocultas para todos menos los hackers serios.


Necesita separar dos preocupaciones:

  1. La clave principal, actualmente un número entero de incremento automático, es la mejor opción para un identificador único simple y relativamente predecible que se puede aplicar en el nivel de la base de datos.

  2. Eso no significa que deba exponerlo a los usuarios en sus URL.

Recomiendo agregar un nuevo campo UUID a su modelo y reasignar sus vistas para usarlo, en lugar del PK, para búsquedas de objetos.


Una solución realmente simple es simplemente encriptar la ID antes de enviarla a una fuente externa. Puedes descifrarlo en el camino de regreso.