【问题标题】:Django Duplicate Key Error but key does not existDjango重复键错误但键不存在
【发布时间】:2014-06-05 14:35:55
【问题描述】:

我正在测试一个小应用程序,我在其中扩展了用户模型以添加一些附加信息。用户可以通过 django-allauth (google OAuth2) 很好地注册,并将条目添加到他们的数据库中。

class LabUser(models.Model):
    user = models.OneToOneField(User)
    verified = models.BooleanField(default=False, blank=False)
    phone_number = models.CharField(max_length=30, verbose_name="Phone Number", null=True, blank=True)
    home_phone = models.CharField(max_length=30,verbose_name="Home Phone", null=True, blank=True)

    def __unicode__(self):
        return self.user.username

但是,当我尝试使用 django-admin 面板添加另一个用户时,我收到以下数据库错误:

IntegrityError: duplicate key value violates unique constraint "lab_manager_labuser_user_id_key"
DETAIL:  Key (user_id)=(24) already exists.

检查 psql 中的表后,我看到以下内容:

l=# select id, username from auth_user;
 id |    username
----+----------------
 13 | xxxx
 23 | xxxx
 18 | xxxx
 12 | xxxx
 21 | xxxx
 14 | xxxx
 22 | xxxx
  1 | xxxx
(8 rows)

l=# select id, user_id from lab_manager_labuser;
 id | user_id
----+---------
  9 |      13
  1 |       1
 16 |      18
  8 |      12
 10 |      14
 21 |      21
 22 |      22
 23 |      23
(8 rows)

查看我的序列表,我可以看到这些值高于它们各自的模型表中的值:

l=# SELECT sequence_name, last_value FROM auth_user_id_seq;
  sequence_name   | last_value
------------------+------------
 auth_user_id_seq |         24
(1 row)

l=# SELECT sequence_name, last_value FROM lab_manager_labuser_id_seq;
       sequence_name        | last_value
----------------------------+------------
 lab_manager_labuser_id_seq |         25
(1 row)

我查看了this 类似的问题,但找不到在我的情况下应该发生这种冲突的原因。这两个序列似乎都领先于我表中的值。

即我尝试了以下方法,虽然它增加了我的密钥,但它仍然会导致所谓的完整性错误,而这些密钥显然不存在于数据库中。

SELECT setval('lab_manager_labuser_id_seq', (SELECT MAX(user_id) from lab_manager_labuser)+1)")

任何帮助将不胜感激。

更新:

我设置了一个接收器来创建一个labuser模型,如下所示,也许我这样做不正确,导致数据库不匹配:

@receiver(post_save, sender=User)
def add_labuser(sender, created, instance, **kwargs):
    if created:
        LabUser.objects.create(user=instance)

我认为这一定是问题的一部分,因为我可以在 shell 中使用以下代码成功创建用户对象:

a = User()
##(add fields)##
a.save()
LabUser.objects.create(user=a)

重复键错误发生的原因是因为我通过管理面板添加用户,内联如下:

class LabUserInline(admin.StackedInline):
    model = LabUser
    can_delete = False

显然,当通过 Admin 创建用户时,会在调用 save 之前通过内联自动创建 LabUser 记录。然后当接收到 post_save 信号时,django 会尝试为同一个 User 对象创建另一个 LabUser 记录,在该记录上发生键冲突。

有人知道如何规避这种冗余吗?

【问题讨论】:

  • 看来这个问题的原因不在贴出的代码中。我猜lab_manager_labuser 被插入了两次,导致了这个问题。没有user_id=24 的条目让我认为您正在使用事务并且错误时所有内容都会回滚。
  • 我认为您正在做一些事情,但这似乎也与我在创建新用户时保存连接的 LabUser 对象的方式有关。我意识到我实际上可以从外壳创建对象。在@ElmoVanKielmo 上方查看我的更新
  • 您更新中的代码是有意义的,应该不会导致这个问题...
  • @ElmoVanKielmo 进行了更新。任何建议表示赞赏。

标签: python database django postgresql django-allauth


【解决方案1】:

我发现了这个问题。虽然当用户通过注册表单注册时 django-allauth 默认没有创建 LabUser 对象,但 django 管理面板 ,因为我已经实现了以下内联。

class LabUserInline(admin.StackedInline):
    model = LabUser
    can_delete = False
    verbose_name = 'Lab User'
    verbose_name_plural = 'Lab Users'

此外,它在调用 user.save() 之前创建了这个对象,所以当我的 post_save 信号被发送并调用接收器时,LabUser 对象已经存在导致键冲突。

我通过删除保存后信号来解决此问题,并简单地在 django-allauth 的 SignUp 表单的 save() 方法中创建对象。

    def save(self, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()
        labuser = LabUser.objects.create(user=user)
        ...populate fields...
        labuser.save()

【讨论】:

    猜你喜欢
    • 2016-11-18
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 2015-02-17
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    相关资源
    最近更新 更多