get() 返回了多个模型——它返回了 4 个! - 但数据库中只有 1 个模型
get() returned more than one Model -- it returned 4! - but only 1 Model in db
在 Django 视图中,我使用 get_or_create 时收到 MultipleObjectsReturned 错误。 Many questions have been asked 关于这个,但那些人似乎错误地使用了这个结构。我认为我使用正确,但如果我错了请纠正我。
我有以下型号:
class MyModel(models.Model):
field1 = models.ForeignKey('app.OtherModel', on_delete=models.SET_NULL, null=True)
field2 = models.CharField(max_length=50, blank=True, null=True)
field3 = models.JSONField()
field4 = models.JSONField()
updated = models.DateTimeField(_('Date updated'), auto_now=True)
created = models.DateTimeField(_('Date created'), default=timezone.now)
...
class Meta:
ordering = ('-created',)
constraints = [
models.UniqueConstraint(fields=['field1', 'field2'], name='unique_model'),
]
我认为这段代码:
o, created = MyModel.objects.get_or_create(field1=value1, field2='value2',
defaults={'field3': '...', 'field4': '...'})
所以 get
是在 UniqueConstraint
中的两个字段上执行的,其他字段在 defaults
部分中列出(如果 create
是必需的)。
当 Django 执行 get_or_create
的 get
部分时,在最后一行抛出 MultipleObjectsReturned,但仅在极少数情况下 - 大多数情况下此构造运行良好。
该视图实际上是 AWS 的 SNS 事件消息处理程序,用于 SES,处理交付延迟、退回和投诉。在 理论 中,不应恰好调用此视图两次,从而导致竞争条件。当我检查服务器日志时,我确实 没有 在抛出此错误时看到对该视图的多次调用。 (只发送了一封电子邮件,SNS 仅每 20 秒尝试重新发送一次 SNS 事件。)
This article很好地解释了问题,但不幸的是没有为我提供解决方案。
请注意,有时“-- 它返回了 2!”,有时“-- 它返回了 3!”有时“——它返回 4!”
另请注意,不是 None 的字段会引发错误:字段 1 的 ForeignKey
指向“OtherModel”中的一行,并且field2 的 CharField
包含一个字符串。
更重要的是:当我在生产中 运行 ipython 中的相同 get_or_create 时,使用与错误中提到的完全相同的值(在 Sentry 中),没有错误抛出并正确执行 get
。
视图中出现什么问题以及为什么会出现问题?
我正在使用 Django 3.2.2 和 Postgres 11.10。
在SQL数据库系统中,NULL
不等于NULL
(通常是NULL = NULL
returnsNULL
),因此如果这些字段是 nullable,这意味着另一个字段可以重复任意次数。
我们可以做的是实现三个约束:两个字段的唯一性,一个字段的唯一性,如果另一个是 NULL
,反之亦然,那么约束看起来像:
from django.db.models import Q, UniqueConstraint
class MyModel(models.Model):
field1 = models.ForeignKey(
'app.OtherModel',
on_delete=models.SET_NULL,
null=True
)
field2 = models.CharField(max_length=50, blank=True, null=True)
# …
class Meta:
ordering = ('-created',)
constraints = [
UniqueConstraint(fields=['field1', 'field2'], name='unique_model1'),
UniqueConstraint(fields=['field1']<strong>, condition=Q(field2=None)</strong>, name='unique_model2'),
UniqueConstraint(fields=['field2']<strong>, condition=Q(field1=None)</strong>, name='unique_mode3'),
]
然而,并非所有数据库后端都可能强制执行带有条件的唯一约束。
在 Django 视图中,我使用 get_or_create 时收到 MultipleObjectsReturned 错误。 Many questions have been asked 关于这个,但那些人似乎错误地使用了这个结构。我认为我使用正确,但如果我错了请纠正我。
我有以下型号:
class MyModel(models.Model):
field1 = models.ForeignKey('app.OtherModel', on_delete=models.SET_NULL, null=True)
field2 = models.CharField(max_length=50, blank=True, null=True)
field3 = models.JSONField()
field4 = models.JSONField()
updated = models.DateTimeField(_('Date updated'), auto_now=True)
created = models.DateTimeField(_('Date created'), default=timezone.now)
...
class Meta:
ordering = ('-created',)
constraints = [
models.UniqueConstraint(fields=['field1', 'field2'], name='unique_model'),
]
我认为这段代码:
o, created = MyModel.objects.get_or_create(field1=value1, field2='value2',
defaults={'field3': '...', 'field4': '...'})
所以 get
是在 UniqueConstraint
中的两个字段上执行的,其他字段在 defaults
部分中列出(如果 create
是必需的)。
当 Django 执行 get_or_create
的 get
部分时,在最后一行抛出 MultipleObjectsReturned,但仅在极少数情况下 - 大多数情况下此构造运行良好。
该视图实际上是 AWS 的 SNS 事件消息处理程序,用于 SES,处理交付延迟、退回和投诉。在 理论 中,不应恰好调用此视图两次,从而导致竞争条件。当我检查服务器日志时,我确实 没有 在抛出此错误时看到对该视图的多次调用。 (只发送了一封电子邮件,SNS 仅每 20 秒尝试重新发送一次 SNS 事件。)
This article很好地解释了问题,但不幸的是没有为我提供解决方案。
请注意,有时“-- 它返回了 2!”,有时“-- 它返回了 3!”有时“——它返回 4!”
另请注意,不是 None 的字段会引发错误:字段 1 的 ForeignKey
指向“OtherModel”中的一行,并且field2 的 CharField
包含一个字符串。
更重要的是:当我在生产中 运行 ipython 中的相同 get_or_create 时,使用与错误中提到的完全相同的值(在 Sentry 中),没有错误抛出并正确执行 get
。
视图中出现什么问题以及为什么会出现问题?
我正在使用 Django 3.2.2 和 Postgres 11.10。
在SQL数据库系统中,NULL
不等于NULL
(通常是NULL = NULL
returnsNULL
),因此如果这些字段是 nullable,这意味着另一个字段可以重复任意次数。
我们可以做的是实现三个约束:两个字段的唯一性,一个字段的唯一性,如果另一个是 NULL
,反之亦然,那么约束看起来像:
from django.db.models import Q, UniqueConstraint
class MyModel(models.Model):
field1 = models.ForeignKey(
'app.OtherModel',
on_delete=models.SET_NULL,
null=True
)
field2 = models.CharField(max_length=50, blank=True, null=True)
# …
class Meta:
ordering = ('-created',)
constraints = [
UniqueConstraint(fields=['field1', 'field2'], name='unique_model1'),
UniqueConstraint(fields=['field1']<strong>, condition=Q(field2=None)</strong>, name='unique_model2'),
UniqueConstraint(fields=['field2']<strong>, condition=Q(field1=None)</strong>, name='unique_mode3'),
]
然而,并非所有数据库后端都可能强制执行带有条件的唯一约束。