Django和Postgres中的get_or_create失败(违反唯一约束条件的重复键值)

13

感谢您抽出时间阅读我的问题。

我有一个 Django 应用程序,其中包含以下模型:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    ...

class Visit(models.Model):
    profile = models.ForeignKey(UserProfile)
    date = models.DateField(auto_now_add=True, db_index=True)
    ip = models.IPAddressField()
    class Meta:
        unique_together = ('profile', 'date', 'ip')
在视图中:
profile = get_object_or_404(Profile, pk = ...)
get, create = Visit.objects.get_or_create(profile=profile, date=now.date(), ip=request.META['REMOTE_ADDR'])
if create: DO SOMETHING

除了Postgres日志中充满了重复键错误之外,一切都正常:

2012-02-15 14:13:44 CET ERROR:  duplicate key value violates unique constraint "table_visit_profile_id_key"
2012-02-15 14:13:44 CET STATEMENT:  INSERT INTO "table_visit" ("profile_id", "date", "ip") VALUES (1111, E'2012-02-15', E'xx.xx.xxx.xxx') RETURNING "table_visit"."id"

尝试了不同的解决方案,例如:

from django.db import transaction 
from django.db import IntegrityError

@transaction.commit_on_success
def my_get_or_create(prof, ip):    
    try:
        object = Visit.objects.create(profile=prof, date=datetime.now().date(), ip=ip)
    except IntegrityError:
        transaction.commit()
        object = Visit.objects.get(profile=prof, date=datetime.now().date(), ip=ip)
    return object

....

created = my_get_or_create(prof, request.META['REMOTE_ADDR'])
if created: DO SOMETHING

这个只对MySQL有效吗?有人知道如何避免在Postgres中重复键值错误吗?


https://dev59.com/eXVC5IYBdhLWcg3wlyQo - user
4个回答

12

get_or_create()中出现这些错误的另一个可能原因是在搜索字段中存在数据类型不匹配 - 例如将False而不是None传递到可空字段中。 .get_or_create()内部的.get()将找不到它,Django将继续创建新行 - 这将由于PostgreSQL约束而失败。


1
谢谢你的回答 :) - Barmi
结束了原本可能让我头疼一整个上午的问题。 - alphazwest
我同意你的观点,当你设置查询如下:obj, created = UserPermission.objects.get_or_create( user=user, boolean_field=boolean_field)如果布尔字段为False,它将始终尝试创建对象并引发: duplicate key value violates unique constraint 因为你已经定义了 boolean_field=models.BooleanField(default=False) - Tarek Kalaji

4

在使用postgres时,我使用get_or_create遇到了一些问题。最终我放弃了样板代码,改用传统的方式:

try:
    jobInvite  = Invite.objects.get(sender=employer.user, job=job)
except Invite.DoesNotExist:
    jobInvite  = Invite(sender=employer.user, job=job)
    jobInvite.save()
# end try

8
虽然这个解决方案在大多数网站上看起来是有效的,但在高流量的网站上会失败,因为那里有很多并发请求同时工作。 - Simon Steinberger

1

你是否曾经在Visit的个人资料字段上设置了unique=True?

看起来Postgres已经生成了一个唯一约束,仍然有效。 "table_visit_profile_id_key"是它自动生成的名称,如果您记录了用户的多次访问,自然会引起这些错误。

如果是这种情况,您是否使用South来管理数据库更改?如果没有,请下载!


0

PostgreSQL在一些微妙的查询中的行为有些不同,这导致了IntegrityError错误,尤其是在切换到Django 1.6之后。解决方法如下-您需要在每个出错的模型中添加select_on_save选项:

class MyModel(models.Model):
     ...
     class Meta:
         select_on_save = True

这里有文档: Options.select_on_save


1
如果我正在使用Django 1.4,而且我遇到了完整性错误,该怎么办? - nu everest

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接