生成随机字母数字字符串作为模型的主键

时间:2022-11-24 21:45:27

I would like a model to generate automatically a random alphanumeric string as its primary key when I create a new instance of it.

我希望一个模型在我创建一个新的实例时自动生成一个随机的字母数字字符串作为其主键。

example:

例:

from django.db import models

class MyTemporaryObject(models.Model):
    id = AutoGenStringField(lenght=16, primary_key=True)
    some_filed = ...
    some_other_field = ...

in my mind the key should look something like this "Ay3kJaBdGfcadZdao03293". It's for very temporary use. In case of collision I would like it Django to try a new key.

在我看来,键应该看起来像这样的“Ay3kJaBdGfcadZdao03293”。这是非常临时使用的。如果发生碰撞,我希望Django尝试新密钥。

I was wondering if there was already something out there, or a very simple solution I am not seeing (I am fairly new to python and Django). Otherwise I was thinking to do my own version of models.AutoField, would that be the right approach?

我想知道是否已经存在某些东西,或者我没有看到一个非常简单的解决方案(我对python和Django来说相当新)。否则我想要做我自己的模型版本.AutoField,这是正确的方法吗?

I have already found how to generate the key here, so it's not about the string generation. I would just like to have it work seamlessly with a simple Django service without adding too much complexity to the code.

我已经找到了如何在这里生成密钥,所以它不是关于字符串生成的。我只想让它与简单的Django服务无缝协作,而不会给代码增加太多的复杂性。

EDIT: Possible solution? What do you think?

编辑:可能的解决方案?你怎么看?

id = models.CharField(unique=True, primary_key=True, default=StringKeyGenerator(), editable=False)

with

class StringKeyGenerator(object):
    def __init__(self, len=16):
        self.lenght = len
    def __call__(self):
        return ''.join(random.choice(string.letters + string.digits) for x in range(self.lenght))

I came up with it after going through the Django documentation one more time.

在再次浏览Django文档之后我想出了它。

3 个解决方案

#1


4  

Here's how I would do it without making the field a primary key:

这是我如何在不将字段作为主键的情况下执行此操作:

from django.db import IntegrityError

class MyTemporaryObject(models.Model):
    auto_pseudoid = models.CharField(max_length=16, blank=True, editable=False, unique=True)
    # add index=True if you plan to look objects up by it
    # blank=True is so you can validate objects before saving - the save method will ensure that it gets a value

    # other fields as desired

    def save(self, *args, **kwargs):
        if not self.auto_pseudoid:
            self.auto_pseudoid = generate_random_alphanumeric(16)
            # using your function as above or anything else
        success = False
        failures = 0
        while not success:
            try:
                super(MyTemporaryObject, self).save(*args, **kwargs)
            except IntegrityError:
                 failures += 1
                 if failures > 5: # or some other arbitrary cutoff point at which things are clearly wrong
                     raise
                 else:
                     # looks like a collision, try another random value
                     self.auto_pseudoid = generate_random_alphanumeric(16)
            else:
                 success = True

Two problems that this avoids, compared to using the field as the primary key are that:

与使用字段作为主键相比,这避免了两个问题:

1) Django's built in relationship fields require integer keys

1)Django的内置关系字段需要整数键

2) Django uses the presence of the primary key in the database as a sign that save should update an existing record rather than insert a new one. This means if you do get a collision in your primary key field, it'll silently overwrite whatever else used to be in the row.

2)Django使用数据库中主键的存在作为save应该更新现有记录而不是插入新记录的标志。这意味着如果您在主键字段中发生冲突,它将以静默方式覆盖该行中的其他任何内容。

#2


14  

One of the simplest way to generate unique strings in python is to use uuid module. If you want to get alphanumeric output, you can simply use base64 encoding as well:

在python中生成唯一字符串的最简单方法之一是使用uuid模块。如果你想获得字母数字输出,你也可以简单地使用base64编码:

import uuid
import base64
uuid = base64.b64encode(uuid.uuid4().bytes).replace('=', '')
# sample value: 1Ctu77qhTaSSh5soJBJifg

You can then put this code in the model's save method or define a custom model field using it.

然后,您可以将此代码放在模型的save方法中,或使用它定义自定义模型字段。

#3


2  

Try this:

尝试这个:

The if statement below is to make sure that the model is update able.

下面的if语句是为了确保模型能够更新。

Without the if statement you'll update the id field everytime you resave the model, hence creating a new model everytime

如果没有if语句,每次重新保存模型时都会更新id字段,因此每次都会创建一个新模型

from uuid import uuid4
from django.db import IntegrityError

class Book(models.Model):
    id = models.CharField(primary_key=True, max_length=32)

    def save(self, *args, **kwargs):
        if self.id:
            super(Book, self).save(*args, **kwargs)
            return

        unique = False
        while not unique:
            try:
                self.id = uuid4().hex
                super(Book, self).save(*args, **kwargs)
            except IntegrityError:
                self.id = uuid4().hex
            else:
                unique = True

#1


4  

Here's how I would do it without making the field a primary key:

这是我如何在不将字段作为主键的情况下执行此操作:

from django.db import IntegrityError

class MyTemporaryObject(models.Model):
    auto_pseudoid = models.CharField(max_length=16, blank=True, editable=False, unique=True)
    # add index=True if you plan to look objects up by it
    # blank=True is so you can validate objects before saving - the save method will ensure that it gets a value

    # other fields as desired

    def save(self, *args, **kwargs):
        if not self.auto_pseudoid:
            self.auto_pseudoid = generate_random_alphanumeric(16)
            # using your function as above or anything else
        success = False
        failures = 0
        while not success:
            try:
                super(MyTemporaryObject, self).save(*args, **kwargs)
            except IntegrityError:
                 failures += 1
                 if failures > 5: # or some other arbitrary cutoff point at which things are clearly wrong
                     raise
                 else:
                     # looks like a collision, try another random value
                     self.auto_pseudoid = generate_random_alphanumeric(16)
            else:
                 success = True

Two problems that this avoids, compared to using the field as the primary key are that:

与使用字段作为主键相比,这避免了两个问题:

1) Django's built in relationship fields require integer keys

1)Django的内置关系字段需要整数键

2) Django uses the presence of the primary key in the database as a sign that save should update an existing record rather than insert a new one. This means if you do get a collision in your primary key field, it'll silently overwrite whatever else used to be in the row.

2)Django使用数据库中主键的存在作为save应该更新现有记录而不是插入新记录的标志。这意味着如果您在主键字段中发生冲突,它将以静默方式覆盖该行中的其他任何内容。

#2


14  

One of the simplest way to generate unique strings in python is to use uuid module. If you want to get alphanumeric output, you can simply use base64 encoding as well:

在python中生成唯一字符串的最简单方法之一是使用uuid模块。如果你想获得字母数字输出,你也可以简单地使用base64编码:

import uuid
import base64
uuid = base64.b64encode(uuid.uuid4().bytes).replace('=', '')
# sample value: 1Ctu77qhTaSSh5soJBJifg

You can then put this code in the model's save method or define a custom model field using it.

然后,您可以将此代码放在模型的save方法中,或使用它定义自定义模型字段。

#3


2  

Try this:

尝试这个:

The if statement below is to make sure that the model is update able.

下面的if语句是为了确保模型能够更新。

Without the if statement you'll update the id field everytime you resave the model, hence creating a new model everytime

如果没有if语句,每次重新保存模型时都会更新id字段,因此每次都会创建一个新模型

from uuid import uuid4
from django.db import IntegrityError

class Book(models.Model):
    id = models.CharField(primary_key=True, max_length=32)

    def save(self, *args, **kwargs):
        if self.id:
            super(Book, self).save(*args, **kwargs)
            return

        unique = False
        while not unique:
            try:
                self.id = uuid4().hex
                super(Book, self).save(*args, **kwargs)
            except IntegrityError:
                self.id = uuid4().hex
            else:
                unique = True