代码之家  ›  专栏  ›  技术社区  ›  rdrey

使用随机主键创建Django对象

  •  9
  • rdrey  · 技术社区  · 14 年前

    我正在使用一个API,它希望我为使用其API的事务生成不透明的“引用ID”,换句话说,就是用户无法以任何方式猜测或推断的唯一引用。(infer是正确的英语吗?)

    这就是我目前所破解的:

    randomRef = randint(0, 99999999999999)
    while Transaction.objects.filter(transactionRef = randomRef).count():
        randomRef = randint(0, 99999999999999)
    
    Transaction.objects.create(user=user, transactionRef=randomRef, price=999)
    

    不幸的是,我的数据库目前似乎缺少事务。我已经意识到我的方法不是特别安全的线程(比如我在多个mod wsgi-apache线程上运行相同的django代码,它们都可能生成相同的randomref!)

    有没有人有更好的技巧来为我生成随机主键?

    6 回复  |  直到 7 年前
        1
  •  16
  •   Amber    14 年前

    为什么不只是加密普通的顺序ID呢?对于不知道加密密钥的人来说,ID似乎也是随机的。您可以编写一个包装器,它在到数据库的路上自动解密ID,并在从数据库来的路上对其进行加密。

        2
  •  11
  •   Trey Hunner    14 年前

    我根据这个问题创建了一个要点: https://gist.github.com/735861

    按照Amber的建议,使用des对私钥进行加密和解密。加密密钥以基36表示,但只要表示是唯一的,任何其他基于字符的表示都可以工作。

    任何需要这种加密私钥表示的模型只需要继承代码中显示的模型和管理器。

    这是代码的要点:

    import struct
    from Crypto.Cipher import DES
    from django.db import models
    
    class EncryptedPKModelManager(models.Manager):
        """Allows models to be identified based on their encrypted_pk value."""
        def get(self, *args, **kwargs):
            encrypted_pk = kwargs.pop('encrypted_pk', None)
            if encrypted_pk:
                kwargs['pk'] = struct.unpack('<Q', self.model.encryption.decrypt(
                    struct.pack('<Q', encrypted_pk)
                ))[0]
            return super(EncryptedPKModelManager, self).get(*args, **kwargs)
    
    
    class EncryptedPKModel(models.Model):
        """Adds encrypted_pk property to children."""
        encryption = DES.new('8charkey') # Change this 8 character secret key
    
        def _encrypted_pk(self):
            return struct.unpack('<Q', self.encryption_obj.encrypt(
                str(struct.pack('<Q', self.pk))
            ))[0]
    
        encrypted_pk = property(_encrypted_pk)
    
        class Meta:
            abstract = True
    

    对于一个 Transaction 对象调用 transaction , transaction.encrypted_pk 将返回私钥的加密表示形式。 Transaction.objects.get(encrypted_pk=some_value) 将基于加密的私钥表示来搜索对象。

    应该注意的是,此代码假定只适用于可以正确表示为长值的私钥。

        3
  •  6
  •   xster    12 年前

    jbrendel 创建了一个类,您可以简单地继承它来获取自定义ID。

    https://github.com/jbrendel/django-randomprimary

        4
  •  2
  •   Jesse Dhillon    14 年前

    随机整数不唯一,主键必须唯一。将关键字字段设为char(32),然后尝试此操作:

    from uuid import uuid4 as uuid
    randomRef = uuid().hex
    

    UUIDs 非常擅长提供独特性。

        5
  •  2
  •   Michael Williamson    14 年前

    您应该能够将数据库中的TransactionRef列设置为唯一的。这样,数据库将不允许添加具有相同transactionRef值的事务。一种可能性是 randomly generate UUIDs --随机UUID碰撞的概率非常小。

        6
  •  2
  •   Alex Martelli    14 年前

    os.urandom(n) 可以“返回一个n个随机字节的字符串,适合加密使用”。只要确保 n 足够大 2**(8*n) 成为 井上 要识别的“唯一”键的数量的平方,可以使碰撞风险尽可能低。例如,如果你认为最终可能会有10亿笔交易(大约 2**30 ) n=8 可能足够了(但要安全地使用一个较大的 n 无论如何,-)