python एक अलग पूर्णांक के साथ Django की प्राथमिक कुंजी को कैसे बदलें जो उस तालिका के लिए अद्वितीय है




mysql hash (4)

मेरे पास एक Django वेब एप्लिकेशन है जो प्राथमिक कुंजी के रूप में डिफ़ॉल्ट ऑटो-इंक्रीमेंट किए गए सकारात्मक पूर्णांक का उपयोग करता है। इस कुंजी का उपयोग पूरे एप्लिकेशन में किया जाता है और इसे अक्सर URL में डाला जाता है। मैं इस संख्या को जनता के सामने उजागर नहीं करना चाहता ताकि वे मेरे डेटाबेस में उपयोगकर्ताओं या अन्य संस्थाओं की संख्या का अनुमान लगा सकें।

यह एक लगातार आवश्यकता है और मैंने सवालों के जवाब के साथ इसी तरह के खदान को देखा है। अधिकांश समाधान मूल प्राथमिक कुंजी मूल्य हैशिंग की सलाह देते हैं। हालांकि, उन जवाबों में से कोई भी मेरी जरूरत के अनुरूप नहीं है। ये मेरी आवश्यकताएं हैं:

  1. मैं प्राथमिक कुंजी फ़ील्ड प्रकार को पूर्णांक के रूप में रखना चाहूंगा।
  2. मैं इस मूल्य को हर बार पढ़ने या लिखने या डेटाबेस की तुलना में हैश / अनहैश न करना पसंद करूंगा। यह बेकार लगता है: यह सिर्फ एक बार करना अच्छा होगा: जब रिकॉर्ड शुरू में डेटाबेस में डाला जाता है
  3. हैशिंग / एन्क्रिप्शन फ़ंक्शन को प्रतिवर्ती होने की आवश्यकता नहीं है क्योंकि मुझे मूल अनुक्रमिक कुंजी को पुनर्प्राप्त करने की आवश्यकता नहीं है। हैशेड मूल्य को केवल विशिष्ट होना चाहिए।
  4. हैशेड मूल्य केवल उस तालिका के लिए अद्वितीय होने की आवश्यकता है - सार्वभौमिक रूप से अद्वितीय नहीं।
  5. हैशेड मान जितना संभव हो उतना कम होना चाहिए। मैं बहुत लंबे (20+ वर्ण) URL से बचना चाहूंगा

इसे प्राप्त करने का सबसे अच्छा तरीका क्या है? निम्नलिखित कार्य करेगा?

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)

विचार

मैं आपको उसी दृष्टिकोण की सिफारिश Instragam जिसका उपयोग Instragam द्वारा किया जाता है। उनकी आवश्यकताओं को बारीकी से तुम्हारा पालन करने लगता है।

जेनरेट की गई आईडी समय के अनुसार क्रमबद्ध होनी चाहिए (इसलिए फोटो आईडी की एक सूची, उदाहरण के लिए, तस्वीरों के बारे में अधिक जानकारी प्राप्त किए बिना सॉर्ट की जा सकती है) आईडी आदर्श रूप से 64 बिट्स होनी चाहिए (छोटे सूचकांक के लिए, और रेडिस जैसी प्रणालियों में बेहतर भंडारण) प्रणाली संभव के रूप में कुछ नए 'चलती भागों' के रूप में पेश करना चाहिए-हम बहुत कम इंजीनियरों के साथ Instagram को स्केल करने में सक्षम होने का एक बड़ा हिस्सा सरल, आसानी से समझने वाले समाधानों को चुनकर है, जिन पर हम भरोसा करते हैं।

वे एक ऐसी प्रणाली के साथ आए, जिसमें टाइमस्टैम्प पर आधारित 41 बिट्स, 13 ओ डेटाबेस शार्द और 10 ऑटो वृद्धि के हिस्से के लिए हैं। ईमानदारी से आप शार्प का उपयोग नहीं करते हैं। आपके पास एक समय के आधार पर कुल 41 बिट्स और यादृच्छिक पर चुने गए 23 बिट्स हो सकते हैं। यदि आप एक ही समय में रिकॉर्ड सम्मिलित करते हैं, तो संघर्ष होने की संभावना 8.3 मिलियन में 1 की संभावना नहीं है। लेकिन व्यवहार में आपको यह हिट होने की संभावना नहीं है। कुछ कोड के बारे में सही कैसे:

आईडी जनरेट करना

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 

ध्यान दें, उपरोक्त कोड में START_TIME कुछ आरंभिक समय है। आप time.time () * 1000 का उपयोग कर सकते हैं, मान प्राप्त कर सकते हैं और इसे START_TIME रूप में सेट कर सकते हैं

ध्यान दें कि मैंने जिस reverse_id पोस्ट को पोस्ट किया है वह आपको यह पता लगाने की अनुमति देता है कि रिकॉर्ड किस समय बनाया गया था। यदि आपको उस जानकारी पर नज़र रखने की आवश्यकता है, तो आप इसके लिए कोई अन्य फ़ील्ड जोड़ने के बिना ऐसा कर सकते हैं! तो आपकी प्राथमिक कुंजी वास्तव में इसे बढ़ाने के बजाय आपके भंडारण को बचा रही है!

आदर्श

अब यह है कि आपका मॉडल कैसा दिखेगा।

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

यदि आप django के बाहर अपने डेटाबेस में परिवर्तन करते हैं, तो आपको sql फ़ंक्शन के रूप में make_id के बराबर बनाने की आवश्यकता होगी

एक फुट नोट के रूप में। यह कुछ हद तक Mongodb द्वारा उपयोग किए जाने वाले दृष्टिकोण की तरह है जो प्रत्येक वस्तु के लिए _ID


AUTO_INCREMENT रखें, लेकिन इसे अर्ध-गुप्त तरीके से पास करें: कुकी में। कुकी को स्थापित करने, उसे सेट करने और उसे पढ़ने में थोड़ी सी कोडिंग लगती है। लेकिन कुकीज़ सभी लेकिन गंभीर हैकर्स से छिपी हुई हैं।


एक वास्तव में सरल समाधान केवल बाहरी स्रोत को भेजने से पहले आईडी को एन्क्रिप्ट करना है। आप इसे वापस रास्ते में डिक्रिप्ट कर सकते हैं।


आपको दो चिंताओं को अलग करने की आवश्यकता है:

  1. प्राथमिक कुंजी, वर्तमान में एक ऑटो-इंक्रीमेंटिंग पूर्णांक, एक सरल, अपेक्षाकृत अनुमानित अद्वितीय पहचानकर्ता के लिए सबसे अच्छा विकल्प है जिसे डेटाबेस स्तर पर लागू किया जा सकता है।

  2. इसका मतलब यह नहीं है कि आपको इसे अपने URL में उपयोगकर्ताओं को उजागर करना होगा।

मैं आपके मॉडल में एक नया UUID फ़ील्ड जोड़ने की सलाह दूंगा, और ऑब्जेक्ट लुकअप के लिए PK के बजाय, इसका उपयोग करने के लिए अपने विचारों को फिर से तैयार करूंगा।





primary-key