我希望一个模型在我创建一个新的实例时自动生成一个随机的字母数字字符串作为其主键。
示例:
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
版本,那会是正确的方法吗?
我已经找到了如何生成密钥here,因此它与字符串生成无关。我只想让它与简单的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文档后想出了它。
答案 0 :(得分:15)
在python中生成唯一字符串的最简单方法之一是使用uuid
模块。如果你想获得字母数字输出,你也可以简单地使用base64编码:
import uuid
import base64
uuid = base64.b64encode(uuid.uuid4().bytes).replace('=', '')
# sample value: 1Ctu77qhTaSSh5soJBJifg
然后,您可以将此代码放入模型的save
方法中,或使用它定义自定义模型字段。
答案 1 :(得分:4)
如果不将该字段作为主键,我将如何做到这一点:
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
应该更新现有记录而不是插入新记录的标志。这意味着如果您在主键字段中发生冲突,它将默默地覆盖该行中的其他任何内容。
答案 2 :(得分:2)
试试这个:
下面的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