djangoでオブジェクトの主キーとして設定する一意の乱数をどのように生成しますか?
編集 - 乱数は主キーである必要はありませんが、その番号でオブジェクトを参照/取得/呼び出すことができるように、オブジェクトごとに一意である必要があります。
djangoでオブジェクトの主キーとして設定する一意の乱数をどのように生成しますか?
編集 - 乱数は主キーである必要はありませんが、その番号でオブジェクトを参照/取得/呼び出すことができるように、オブジェクトごとに一意である必要があります。
ランダムに生成された数値の一意性は、乱数を生成するためのスペースの大きさによって制限されます。UUID/GUIDS は 128 ビット長であるため、衝突の可能性は低くなります。ただし、バージョン 4 を除くすべての UUID は完全にランダムではありません (これらの場合でも、1 つのニブルが に固定されてい0x4
ます)。そのため、UUID の 1 つのサブ部分を一意と見なすことはできません (詳細については、GUID に関する Raymond Chen の投稿を参照してください。彼は Microsoft で働いており、彼のコラムのほとんどは Microsoft 関連ですが、これは UUID/GUID を使用するすべてのものに適用されます)。
したがって、この目的で UUID を考慮しないでください。最初に決定する必要があるのは、必要なスペースの大きさです。次に、ニーズに合ったエンコード方式を決定できます。これは、参照したいアイテムの数に大きく依存します。誕生日問題により、ランダムに生成された 2 つの数字が衝突する可能性は驚くほど低く、Wiki の誕生日攻撃ページで概算が提供されています。
H は可能な値の数で、Q(H) は衝突が発生する前に生成できるアイテムの数です。競合を確認するには、おそらくデータベースにアクセスして、生成された番号が存在するかどうかを確認し、存在する場合は別の番号を作成して再度確認する必要があるため、競合は望ましくないと想定します。データベースにアイテムが増えるにつれて、このプロセスにかかる時間はますます長くなります。もちろん、衝突をチェックしたい場合もありますが、複数回チェックする必要がある可能性は非常に低いはずです。
それでは、32 ビット値から始めましょう。上記の式から、衝突が予想される前に約 82,000 個のアイテムが生成されます。数千または数万だけを期待している場合、これは動作する許容可能なビット数である可能性があります。他のいくつかの値については、ビット数に対して生成されるアイテムの量は次のとおりです。
16 bits: 320
24 bits: 5100
32 bits: 82,000
40 bits: 1.3*16^6
48 bits: 2.1*10^7
64 bits: 5.4*10^9
これらの数は、テーブルにあると予想される最大値であると考えています。セキュリティ関連の場合は、必要なものよりもはるかに広い範囲を選択します (外部から見えるユーザー ID で、他の人に推測されたくない場合、最大数百人のユーザーですか? 48 ビットが最小で快適です)そこの)
具体的には、セキュリティ関連以外の項目については random.getrandbits() を使用してこれらの数値を生成し、代わりに ssl.RAND_bytes() を使用します。
ここで、問題の別の部分について説明します。これらのランダムなビットを印刷可能なものにエンコードします。最も基本的なものは 16 進数でエンコードされたもので0-9A-F
、長さは生成したビット数を 4 で割ったものになります (32 ビット識別子は 8 文字、40 ビット 10 などになります)。これは大文字と小文字が区別されず、入力が最も簡単です。
もう 1 つのオプションは、base-64 エンコーディングです。これにより、(1/6)*n (切り上げ) 文字が出力されます (n はビット数)。つまり、32 ビット、6 文字、40 ビット、7 文字などです。Base 64 値は大文字と小文字が区別され、URL に入れる場合は注意が必要です (+ と / は両方とも Base 64 エンコーディングの一部です。および _ に置き換えることができます (たとえば、URL エンコーディングの場合)。これにより、入力が難しくなりますが、値が大きいほど短くなります (base64 の場合は 11 文字、64 ビット値の 16 進の場合は 16 文字であり、この節約は増加します)。
これはあなたの質問に直接答えるものではありませんが (データベースに格納する値を割り当てる方法を知っていると思いますが、これらの値はデータベースにエンコードされた文字列形式で格納するか、BLOB として格納する必要があります)。データベースはこれらの値の一部を符号付きとして扱い、悪いことを引き起こす可能性があるため)、アプリケーションの正しい組み合わせを理解するために知っておくべきことを教えてくれるはずです。
たぶん、uuidを生成するだけですか?
>>> from uuid import uuid4
>>> uid = uuid4()
>>> uid
UUID('88016297-726a-4a42-a5d3-7c1047e27cac')
>>> uid.int
180782199398610579001229174541650132140L
>>> uid.hex
'88016297726a4a42a5d37c1047e27cac'
>>> uid.bytes
'\x88\x01b\x97rjJB\xa5\xd3|\x10G\xe2|\xac'
長いuuidは、衝突を回避し、高度な一意性を保証することを目的としています。より短いIDが必要な場合、これはどのように使用するかによって異なります。ルートURLとして使用するため、すべてのモデルで一意である必要がある場合は、/<slug>/
割り当てる前にデータベースにクエリを実行して一意性を確認する必要があります。
slugfieldと、モデルの保存時に一意のslug値を設定するこのようなスニペットの使用を検討することをお勧めします。
一言で言えば...長いUUID==インスタント値。スラッグ値を短くする==一意性を確認するためのクエリループ。