【问题标题】:How to check if an instance of a model already exist in database如何检查模型的实例是否已存在于数据库中
【发布时间】:2018-03-02 22:29:28
【问题描述】:

所以我定义了一个模型Language。我需要跟踪创建语言对象的人。我也不需要同一种语言的多个实例。出于这个原因,我希望能够在用户尝试创建一种语言时检查一种语言是否已经存在。如果它存在,那么我只会告诉用户他们可以使用已经存在的那个。如果没有,他们可以创建它。我正在使用自定义 User 模型。

USERS = get_user_model().objects.all()
from random import choice

class Language(models.Model):
    language = models.CharField(max_length=25, unique=True)
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    def __str__(self):
        return self.language

    def get_absolute_url(self):
        return reverse('chinemerem:language_index')

    def save(self, *args, **kwargs):
        self.language = self.language.upper()
        super(Language, self).save(*args, **kwargs)

我可以使用try/exceptdjango shell 中创建Language 实例,如下所示。

try:
    language, exists = Language.objects.get_or_create(creator=choice(USERS),
                                language='german')
except IntegrityError:
    pass

但是当我尝试通过 django admin 创建语言实例时,django admin 会抛出 IntegrityError

我的问题:如何在我的模型中实现类似 try/except 块的内容,以检查用户尝试创建语言时是否已经存在语言?

我进行了很多搜索,发现了来自signals 的 pre_save,我怀疑这可能会有所帮助。但是 django 文档几乎没有示例。

任何帮助将不胜感激。

【问题讨论】:

  • 创建一个模型管理器并定义一个管理器方法来检查是否存在匹配的语言。在这种情况下,您总是需要使用 manager 方法进行保存
  • 不相关,但您不应该将查询集创建为模块级变量 - 您最终会得到陈旧的数据
  • Django 的管理员应该已经在检查已经存在的值,因为您在该字段上有 unique=True。你有没有做任何事情来覆盖表单?显示您的管理员课程。
  • @OluwafemiSule 我正在调查。听起来像我需要的。

标签: python django


【解决方案1】:

Your problem comes from the way you call get_or_create() - 您将两个字段作为查找参数传递,因此它首先尝试使用language='german' creator=<youruserhere> 查找Language。如果存在相同的语言但创建者不同,则它与 where 子句不匹配,因此 orm 尝试创建新记录,然后对 language 的唯一约束生效。

解决方案是在字典中将creator 作为defaults 参数传递,以便仅在language 上进行查找,creator 仅在必要时用于创建新记录:

language, created = Language.objects.get_or_create(
   # this is used for lookup (get)
   language="german", 
   # this is used for eventual create
   defaults={'creator':choice(USERS)}
   )

附带说明,如果已创建新记录,则返回元组的第二个元素将是 True,如果找到已存在的记录,则为 False

wrt/ 管理员中的问题,除非您在 ModelAdmin 或表单中做了一些奇怪的事情,或者您没有发布整个模型代码,它应该只是重新显示带有错误消息的表单,而不是引发 IntegrityError (除非最终您在表单验证和实例保存之间遇到竞争条件,但这应该非常非常罕见)。验证期间的唯一性检查是 ModelForm 的默认功能,因此(我能想到的)它不会被应用的唯一原因是在不调用 super().clean() 的情况下覆盖表单的 clean() 方法。

【讨论】:

  • 我所拥有的是整个模型代码。从您的回答中,我知道您并不完全理解我的问题。可能我描述得不好。但是您提醒我,我应该只在调用get_or_create 时使用语言,因为这是我感兴趣的变量,而不是创建者。
  • ...continuation... 但我的问题不在于在django shell 中创建Language 模型。问题是 django 管理员在尝试创建已经存在的语言时抱怨 UNIQUE constraint failed。例如,用户 bruno 第一次创建了Language german。稍后另一个用户 parousia 想要创建相同的 german Language。现在发生了冲突,因为 germanunique。所以问题是:我如何检查 Language 表是否存在 german 并在它已经存在时优雅地退出? 我寻求的是优雅退出
  • @Parousia 正如已经提到的,除非你做了一些奇怪的事情,否则 django 管理员应该在这里做正确的事情 - 重新显示带有错误消息的表单。我只是仔细检查了一下,它开箱即用。如果你得到一个 500 错误,那么你的代码中还有一些你没有发布的东西,或者在你的安装中的某个地方出现了问题(除非最终你在表单验证和实例保存之间遇到了竞争条件,但这应该是非常特殊的)。
  • 作为旁注:您提到“您的用户”和“django 管理员” - 除非“您的用户”是指“您的站点管理员”,否则您做错了,Django 管理员有它的设计并未考虑到最终用户,而是针对站点管理员的。但无论如何:唯一检查不是由管理员自己处理,而是由 ModelForm 处理,所以在这里不会有任何区别。
【解决方案2】:

感谢大家。以下是我使用模型管理器解决问题的方法。

我使用了两个模型管理器。我使用objects = models.Manager() 作为模型管理器之一,这样我就不需要重写很多代码了。

from django.db import models
class LanguageManager(models.Manager):
    def language_exists(self, language):
        return super(LanguageManager, self).get_queryset().filter(language=language.upper()).exists()

class Language(models.Model):
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    language = models.CharField(max_length=25, unique=True)
    objects = models.Manager()
    languages = LanguageManager()

    def __str__(self):
        return self.language

    def get_absolute_url(self):
        return reverse('chinemerem:language_index')

    def save(self, *args, **kwargs):
        self.language = self.language.upper()
        if Language.languages.language_exists(self.language):
            return "Language already exists."
        else:
            super(Language, self).save(*args, **kwargs)

【讨论】:

    【解决方案3】:

    你可以使用exists方法,

    getLanguage = Language.objects.filter(language=request['langugage']).exists()
    
    if not getLanguage:
     # create the model
    

    【讨论】:

      猜你喜欢
      • 2016-03-13
      • 2016-08-08
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      • 2019-09-01
      • 2019-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多