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

50pmv0ei  于 2022-11-04  发布在  PostgreSQL
关注(0)|答案(4)|浏览(312)

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

from django.db import models

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

在我看来,这个键应该看起来像“Ay3kJaBdGfcadZdao03293”。它是非常临时的使用。如果发生冲突,我希望Django尝试一个新的键。
我想知道是否已经有了一些东西,或者一个非常简单的解决方案,我没有看到(我是相当新的Python和Django)。否则,我想做我自己的版本的models.AutoField,这是正确的方法吗?
我已经在这里找到了如何生成密钥,所以这不是字符串的生成,我只想让它与简单的Django服务无缝地工作,而不增加代码的复杂性。
编辑:可能的解决方案?你怎么看?

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

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))

我在浏览了一遍Django的文档后想到了这个方法。

nhaq1z21

nhaq1z211#

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

import uuid
import base64
uuid = base64.b64encode(uuid.uuid4().bytes).replace('=', '')

# sample value: 1Ctu77qhTaSSh5soJBJifg

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

aemubtdh

aemubtdh2#

下面是在不将该字段设为主键的情况下执行此操作的方法:

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

与使用字段作为主键相比,这样可以避免两个问题:
1)Django的内置关系字段需要整数键
2)Django使用数据库中主键的存在作为save应该更新现有记录而不是插入新记录的标志,这意味着如果主键字段发生冲突,它会悄悄地覆盖该行中的其他内容。

wtlkbnrh

wtlkbnrh3#

试试看:
下面的if语句是为了确保模型是可更新的。
如果没有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
rslzwgfq

rslzwgfq4#

下面的代码片段使用Python自带的secrets库,处理id冲突,并在没有id冲突时继续传递完整性错误。
ID 0TCKybG1qgAhRuENyJariA4QN42E9aLfAZOMrzlkJ-RKh4dp的示例

import secrets
from django.db import models, IntegrityError

class Test(models.Model):
    pk = models.CharField(primary_key=True, max_length=32)

    def save(self, *args,**kwargs):
        unique = False
        while not unique:
            try:
                self.pk = secrets.token_urlsafe(12)
                super(Test, self).save(*args,**kwargs)
            except IntegrityError as e :
                # keep raising the exception if it's not id collision error
                if not 'pk' in e.args[0]:
                    unique = True
                    raise e  
            else:
                unique = True

相关问题