Creating django objects with a random primary key

前端 未结 6 1505
面向向阳花
面向向阳花 2021-02-08 12:08

I\'m working with an API that wants me to generate opaque \"reference IDs\" for transactions with their API, in other words, unique references that users can\'t guess or infer i

相关标签:
6条回答
  • 2021-02-08 12:13

    You should be able to set the transactionRef column in your database to be unique. That way, the database will not allow transactions to be added with the same transactionRef value. One possibility is to randomly generate UUIDs -- the probability of random UUIDs colliding is extremely small.

    0 讨论(0)
  • 2021-02-08 12:20

    os.urandom(n) can "Return a string of n random bytes suitable for cryptographic use". Just make sure that n is large enough for 2**(8*n) to be well above the square of the number of "unique" keys you want to identify, and you can make the risk of collision as low as you want. For example, if you think you may end up with maybe a billion transactions (about 2**30), n=8 might suffice (but play it safe and use a somewhat larger n anyway;-).

    0 讨论(0)
  • 2021-02-08 12:24

    I created a gist based on this question: https://gist.github.com/735861

    Following Amber's advice, the private keys are encrypted and decrypted using DES. The encrypted key is represented in base 36, but any other character-based representation will work as long as the representation is unique.

    Any model that would need this kind of encrypted private key representation only needs to inherit from the model and manager shown in the code.

    Here's the meat of the code:

    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
    

    For a Transaction object called transaction, transaction.encrypted_pk would return an encrypted representation of the private key. Transaction.objects.get(encrypted_pk=some_value) would search for objects based on the encrypted private key representation.

    It should be noted that this code assumes only works for private keys that can be represented properly as long values.

    0 讨论(0)
  • 2021-02-08 12:24

    Random integers are not unique, and primary keys must be unique. Make the key field a char(32) and try this instead:

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

    UUIDs are very good at providing uniqueness.

    0 讨论(0)
  • 2021-02-08 12:27

    Why not just encrypt the normal sequential ids instead? To someone who doesn't know the encryption key, the ids will seem just as random. You can write a wrapper that automatically decrypts the ID on the way to the DB, and encrypts it on the way from the DB.

    0 讨论(0)
  • 2021-02-08 12:34

    jbrendel created a class that you can simply inherit to get a custom ID.

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

    0 讨论(0)
提交回复
热议问题