模型字段唯一性验证错误处理

Posted

技术标签:

【中文标题】模型字段唯一性验证错误处理【英文标题】:Model Field uniqueness validation error handling 【发布时间】:2016-10-22 15:43:18 【问题描述】:

我有一个名为 (unique_id) 的模型字段,它生成一个随机字符串。它在模型调用 Posts 中定义。

unique_id = models.CharField(db_index=True, default=generate_id())

generate_id 函数是

import random
a = 'ABCDEFJHIJKLMNOPQRSTUVWXYZabcdefjhijklmnopqrstuvwxyz_-123456789'       

def generate_id(length=6):
        return ''.join(random.choice(a) for i in range(length))

如果 IntegrityError: UNIQUE constraint failed 不是唯一的,django 将引发它。

在数据库级别避免重复唯一 IDS 的正确方法是什么?

【问题讨论】:

请注意,您不应该在默认属性中调用该函数;只需传递可调用对象本身。 感谢您的提醒。 【参考方案1】:

首先正如@DanielRoseman 所说,不要在default 中调用函数,您可以将default 的值设置为可调用的,这样会更好,因为它会在每次创建对象时调用函数,但是如果您在字段定义中调用它将在解释时间调用一次

unique_id = models.CharField(db_index=True, default=generate_id)

其次,我建议使用UUIDField,它会为您生成随机字符串。

为了解决您的问题,您有两种方法。生成相当长的unique_id,至少 12 个字符长,这几乎可以保证您不会发生任何冲突。或者您可以在 generate_id 函数中添加数据库检查

def generate_id(length=6):
    unique_id = ''.join(random.choice(a) for i in range(length))
    while Mymodel.objects.filter(unique_id=unique_id).exists():
        unique_id = ''.join(random.choice(a) for i in range(length))
    return unique_id

回复评论 方法中的模型可以通过self.__class__ 访问,如下例所示

class Post(models.Model):
    def _generate_id(self, length=6):
        unique_id = ''.join(random.choice(a) for i in range(length))
        while self.__class__.objects.filter(unique_id=unique_id).exists():
            unique_id = ''.join(random.choice(a) for i in range(length))
        return unique_id

但如果它是方法,您将无法将其作为默认值添加到字段中,因为不会有 self 实例。但是你可以尝试使用@classmethod 装饰器。只有在实例化Field 对象之前定义了_generate_id,这才有可能,并且它只适用于@classmethods,因为在python 中类是它们自己的微小命名空间。

class Post(models.Model):
    @classmethod
    def _generate_id(cls, length=6):
        unique_id = ''.join(random.choice(a) for i in range(length))
        while cls.objects.filter(unique_id=unique_id).exists():
            unique_id = ''.join(random.choice(a) for i in range(length))
        return unique_id

    unique_id = models.CharField(db_index=True, default=_generate_id, max_length=12)

但我本人从未在现实生活中尝试过。

【讨论】:

感谢您的澄清,有没有办法从模型本身引用模型(帖子)?我正在尝试定义Post._generate_id 方法,以便可以在模型中将其用作默认值。 @static 类中不能有 generate_id。 阅读更新的答案。通常你不需要使用 generate_id 之类的方法,但是如果你仍然想要你的第二个例子@classmethoddecorator 修正了一些错别字并添加了更多信息 太棒了!非常感谢您的详细回答,我学到了很多!【参考方案2】:

所以你的模型声明的问题是你使用了默认的generate_id(),所以该调用的输出被用作默认值。

试试这个

unique_id = models.CharField(db_index=True, default=generate_id)

这将在每次创建对象时调用生成 id

参考:https://docs.djangoproject.com/en/1.9/ref/models/fields/#default

【讨论】:

不会,逻辑对于您的 generate_id 函数应该是正确的。对于上述问题,它一次又一次地给出相同的值。就像在执行 python 文件时,如果 generate_id 返回 10,那么对于那个特定的相同进程,每个保存的对象都将具有值 10。因此,拥有可调用确保每次创建对象时都会调用该函数

以上是关于模型字段唯一性验证错误处理的主要内容,如果未能解决你的问题,请参考以下文章

处理不同文化的验证的最佳方法是啥

在“422(无法处理的实体)”Laravel 7 中验证错误时出现错误

在 laravel 中更新数据时显示错误的唯一键验证

在Woocommerce结帐页面中设置唯一的验证错误通知

无法使用 try 和 catch 处理 axios 中的 404 错误

Laravel 尝试提供唯一字段验证时出现 sql 错误