在 Python/Django 中生成唯一字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26030811/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:56:32  来源:igfitidea点击:

Generate a Unique String in Python/Django

pythondjango

提问by Yax

What I want is to generate a string(key) of size 5 for my users on my website. More like a BBM PIN.

我想要的是在我的网站上为我的用户生成一个大小为 5 的字符串(密钥)。更像是 BBM PIN。

The key will contain numbers and uppercase English letters:

密钥将包含数字和大写英文字母:

  • AU1B7
  • Y56AX
  • M0K7A
  • AU1B7
  • Y56AX
  • M0K7A

How can I also be at rest about the uniqueness of the strings even if I generate them in millions?

即使我生成数以百万计的字符串,我怎么能对字符串的唯一性保持冷静?

In the most pythonic way possible, how can I do this?

以最 Pythonic 的方式,我该怎么做?

采纳答案by zzart

My favourite is

我最喜欢的是

import uuid 
uuid.uuid4().hex[:6].upper()

If you using django you can set the unique constrain on this field in order to make sure it is unique. https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.unique

如果您使用 django,您可以在此字段上设置唯一约束以确保它是唯一的。https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.unique

回答by nu11p01n73R

Am not sure about any short cryptic ways, but it can be implemented using a simple straight forward function assuming that you save all the generated strings in a set:

我不确定任何简短的神秘方式,但它可以使用一个简单的直接函数来实现,假设您将所有生成的字符串保存在一个集合中:

import random

def generate(unique):
    chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    while True:
        value = "".join(random.choice(chars) for _ in range(5))
        if value not in unique:
            unique.add(value)
            break

unique = set()
for _ in range(10):
    generate(unique)

回答by Tianqi Tong

size = 5
''.join(random.choice(string.letters[26:] + string.digits) for in range(size))

this will generate some short code, but they can be duplicated. so check if they are unique in your database before saving.

这将生成一些短代码,但它们可以被复制。所以在保存之前检查它们在您的数据库中是否唯一。

def generate(size=5):
    code = ''.join(random.choice(string.letters[26:] + string.digits) for in range(size))
    if check_if_duplicate(code):
        return generate(size=5)
    return code

or using django unique constrain, and handle exceptions.

或使用 django 唯一约束,并处理异常。

回答by Michael Stachura

From 3.6 You can use secrets module to generate nice random strings. https://docs.python.org/3/library/secrets.html#module-secrets

从 3.6 开始,您可以使用 secrets 模块生成漂亮的随机字符串。 https://docs.python.org/3/library/secrets.html#module-secrets

import secrets
print(secrets.token_hex(5))

回答by Caveman

If you can afford to lose '8' and '9' in the generated numbers there is a very pythonic solution to getting a truly random number.

如果您可以承受在生成的数字中丢失 '8' 和 '9',那么有一个非常pythonic 的解决方案来获得一个真正的随机数。

import os
import base64

base64.b32encode(os.urandom(3))[:5].decode('utf-8')

Since you are going for uniqueness then you have a problem since 36 * 36 * 36 * 36 * 36 = 60'466'176which will definitely result in collisions if you have millions. Since sets are faster than dicts we do...

由于您要追求唯一性,因此您会遇到问题,因为36 * 36 * 36 * 36 * 36 = 60'466'176如果您有数百万,这肯定会导致冲突。由于集合比字典快,我们这​​样做......

some_dict = set()

def generate():
    return base64.b32encode(os.urandom(3))[:5].decode('utf-8')

def generate_unique():
    string = generate()
    while string not in some_set:
        string = generate()
    some_set.add(string)
    return string

回答by vedant

There is a function in django that does what you're looking for (credits to this answer):

django 中有一个函数可以满足您的要求(归功于此答案):

Django provides the function get_random_string()which will satisfy the alphanumeric string generation requirement. You don't need any extra package because it's in the django.utils.cryptomodule.

>>> from django.utils.crypto import get_random_string
>>> unique_id = get_random_string(length=32)
>>> unique_id
u'rRXVe68NO7m3mHoBS488KdHaqQPD6Ofv'

You can also vary the set of characters with allowed_chars:

>>> short_genome = get_random_string(length=32, allowed_chars='ACTG')
>>> short_genome
u'CCCAAAAGTACGTCCGGCATTTGTCCACCCCT'

Django 提供了get_random_string()满足字母数字字符串生成要求的函数。您不需要任何额外的包,因为它在django.utils.crypto模块中。

>>> from django.utils.crypto import get_random_string
>>> unique_id = get_random_string(length=32)
>>> unique_id
u'rRXVe68NO7m3mHoBS488KdHaqQPD6Ofv'

您还可以使用以下命令更改字符集allowed_chars

>>> short_genome = get_random_string(length=32, allowed_chars='ACTG')
>>> short_genome
u'CCCAAAAGTACGTCCGGCATTTGTCCACCCCT'

回答by mevaka

I have a unique field, named 'systemCode' within a lot of my models. And I am generating this manually, but also sometimes it can take value from user input, so I have to check this value before saving and if it matches , regenerating this value as a unique value.

我的许多模型中有一个独特的字段,名为“ systemCode”。而且我是手动生成的,但有时它也可以从用户输入中获取值,所以我必须在保存之前检查这个值,如果匹配,则将此值重新生成为唯一值。

And this is how I generate unique strings at this scenario :

这就是我在这种情况下生成唯一字符串的方式:



This is my standard class Model :

这是我的标准类 Model :

class ClassOne(models.Model):
   name = models.CharField(max_length=100)
   systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
   ....

I am using save()method to generate and check this systemCodeis unique :

我正在使用save()方法生成并检查此systemCode是否唯一:

    def save(self, *args, **kwargs):
        systemCode = self.systemCode
        if not systemCode:
            systemCode = uuid.uuid4().hex[:6].upper()
        while ClassOne.objects.filter(systemCode=systemCode).exclude(pk=self.pk).exists():
            systemCode = uuid.uuid4().hex[:6].upper()
        self.systemCode = systemCode
        super(ClassOne, self).save(*args, **kwargs)

But I have same systemCodefield in all my Models. So I am using a function to generate value.

但是我的所有模型中都有相同的systemCode字段。所以我正在使用一个函数来产生价值。

So, this is how to generate unique value for all models using saveSystemCode()function :

因此,这是使用saveSystemCode()函数为所有模型生成唯一值的方法:

import uuid 

def saveSystemCode(inClass, inCode, inPK, prefix):
    systemCode = inCode
    if not systemCode:
        systemCode = uuid.uuid4().hex[:6].upper()

    while inClass.objects.filter(systemCode=systemCode).exclude(pk=inPK).exists():
        systemCode = uuid.uuid4().hex[:6].upper()

    return systemCode

class ClassOne(models.Model):
    name = models.CharField(max_length=100)
    systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
    ....

    def save(self, *args, **kwargs):
        self.systemCode = saveSystemCode(ClassOne, self.systemCode, self.pk, 'one_')
        super(ClassOne, self).save(*args, **kwargs)


class ClassTwo(models.Model):
    name = models.CharField(max_length=100)
    systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
    ....

    def save(self, *args, **kwargs):
        self.systemCode = saveSystemCode(ClassTwo, self.systemCode, self.pk, 'two_')
        super(ClassTwo, self).save(*args, **kwargs)

class ClassThree(models.Model):
    name = models.CharField(max_length=100)
    systemCode = models.CharField(max_length=25, blank=True, null=True, unique=True)
    ....

    def save(self, *args, **kwargs):
        self.systemCode = saveSystemCode(ClassThree, self.systemCode, self.pk, 'three_')
        super(ClassThree, self).save(*args, **kwargs)


whileloop in the 'saveSystemCode' function is preventing to save same value again.

' saveSystemCode' 函数中的while循环阻止再次保存相同的值。

回答by Reza Abbasi

A more secure and shorter way of doing is using Django's crypto module.

一种更安全、更短的方法是使用 Django 的加密模块。

from django.utils.crypto import get_random_string
code = get_random_string(5)