MultipleObjects使用get_or_create返回

Bri*_*son 13 django python-2.7

我正在编写一个小的django命令来将数据从json API端点复制到Django数据库中.在我实际创建对象时obj, created = model.objects.get_or_create(**filters),我得到一个MultipleObjectsReturned错误.这对我来说是令人惊讶的,因为我的理解get_or_create是,如果我尝试创建一个已经存在的对象,它将只是"得到"它.

我不确定我正在克隆的数据库的完整性,但即使它中有多个相同的对象,当我将它们加载到我的本地Django数据库时,不应该get_or_create使它成为我永远不会超过一份?

任何人都能解释一下吗?我很乐意提供更多细节,我只是不想让读者陷入困境.

dno*_*zay 25

示例代码

想象一下,你有以下模型:

class DictionaryEntry(models.Model):
    name = models.CharField(max_length=255, null=False, blank=False)
    definition = models.TextField(null=True, blank=False)
Run Code Online (Sandbox Code Playgroud)

和以下代码:

obj, created = DictionaryEntry.objects.get_or_create(
    name='apple', definition='some kind of fruit')
Run Code Online (Sandbox Code Playgroud)

get_or_create

如果您还没有看到以下代码get_or_create:

 # simplified
 def get_or_create(cls, **kwargs):
     try:
         instance, created = cls.get(**kwargs), False
     except cls.DoesNotExist:
         instance, created = cls.create(**kwargs), True
     return instance, created
Run Code Online (Sandbox Code Playgroud)

关于网络服务器......

现在假设您有一个带有2工作进程的Web服务器,它们都有自己的数据库并发访问权限.

 # simplified
 def get_or_create(cls, **kwargs):
     try:
         instance, created = cls.get(**kwargs), False # <===== nope not there...
     except cls.DoesNotExist:
         instance, created = cls.create(**kwargs), True
     return instance, created
Run Code Online (Sandbox Code Playgroud)

如果时间正确(或者错误取决于你想要如何表达这个),两个进程都可以进行查找而不能找到项目.他们都可以创建项目.一切都好...

MultipleObjectsReturned: get() returned more than one KeyValue -- it returned 2!

一切都很好......直到你get_or_create第三次打电话,"第三次是魅力"他们说.

 # simplified
 def get_or_create(cls, **kwargs):
     try:
         instance, created = cls.get(**kwargs), False # <==== kaboom, 2 objects.
     except cls.DoesNotExist:
         instance, created = cls.create(**kwargs), True
     return instance, created
Run Code Online (Sandbox Code Playgroud)

unique_together

你怎么能解决这个问题?也许在数据库级别强制执行约束:

class DictionaryEntry(models.Model):
    name = models.CharField(max_length=255, null=False, blank=False)
    definition = models.TextField(null=True, blank=False)
    class Meta:
        unique_together = (('name', 'definition'),)
Run Code Online (Sandbox Code Playgroud)

回到功能:

 # simplified
 def get_or_create(cls, **kwargs):
     try:
         instance, created = cls.get(**kwargs), False
     except cls.DoesNotExist:
         instance, created = cls.create(**kwargs), True # <==== this handles IntegrityError
     return instance, created
Run Code Online (Sandbox Code Playgroud)

假设你和以前有相同的种族,他们都没有找到该项目并继续插入; 这样做他们将开始交易,其中一个将赢得比赛,而另一个将看到一个IntegrityError.

mysql?

该示例使用a TextField,用于mysql转换为LONGTEXT(在我的情况下).添加unique_together约束失败了syncdb.

django.db.utils.InternalError: (1170, u"BLOB/TEXT column 'definition' used in key specification without a key length")

所以,没有运气,你可能需要MultipleObjectsReturned手动处理.

可能的解决方案

  • 有可能TextField用a 替换CharField.
  • 可以添加一个CharField可以在其中TextField计算pre_save并在其中使用的强哈希值unique_together.


Yuj*_*ita 5

顾名思义,get_or_create model.objects.get()s或model.objects.create()s。

从概念上讲,它等效于:

try:
   model.objects.get(pk=1)
except model.DoesNotExist:
   model.objects.create(pk=1)
Run Code Online (Sandbox Code Playgroud)

来源是您找到这些类型问题的明确答案。提示:搜索def get_or_create。如您所见,此函数仅捕获DoesNotExisttry / except。

def get_or_create(self, **kwargs):
    """
    Looks up an object with the given kwargs, creating one if necessary.
    Returns a tuple of (object, created), where created is a boolean
    specifying whether an object was created.
    """
    assert kwargs, \
            'get_or_create() must be passed at least one keyword argument'
    defaults = kwargs.pop('defaults', {})
    lookup = kwargs.copy()
    for f in self.model._meta.fields:
        if f.attname in lookup:
            lookup[f.name] = lookup.pop(f.attname)
    try:
        self._for_write = True
        return self.get(**lookup), False
    except self.model.DoesNotExist:
Run Code Online (Sandbox Code Playgroud)