用随机主键创建Django对象

11 投票
6 回答
9270 浏览
提问于 2025-04-16 01:36

我正在使用一个API,它要求我为交易生成不透明的“参考ID”,也就是说,这些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 个回答

6

jbrendel 创建了一个类,你只需要继承这个类就能获得一个自定义的ID。

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

15

我根据这个问题创建了一个代码片段: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

对于一个叫做transactionTransaction对象,transaction.encrypted_pk会返回私钥的加密表示。Transaction.objects.get(encrypted_pk=some_value)会根据加密的私钥表示来查找对象。

需要注意的是,这段代码假设只对可以正确表示为长整型的私钥有效。

16

为什么不直接加密普通的顺序ID呢?对那些不知道加密钥匙的人来说,这些ID看起来就像是随机的。你可以写一个工具,在数据发送到数据库的时候自动解密ID,而在从数据库取出数据的时候再加密它。

撰写回答