建立既与InlineAdmin并在Django一个post_save信号轮廓模型(Creating

2019-07-18 06:20发布

我创建了一个“轮廓”模型(具有1对1的关系到用户模型)作为上述扩展现有用户模型 。 轮廓模型有一个可选的多到一个关系到另一种模式:

class Profile(models.Model):
    user = models.OneToOneField(User, primary_key=True)
    account = models.ForeignKey(Account, blank=True, null=True, on_delete=models.SET_NULL)

作为记录都在这里,我还创建了一个在线管理:

class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = 'profiles'
# UserAdmin and unregister()/register() calls omitted, they are straight copies from the Django docs

现在,如果我不选择一个account创建用户时,在管理,配置文件模式不会被创建。 于是我连接到post_save信号,又正好文档以下内容:

@receiver(post_save, sender=User)
def create_profile_for_new_user(sender, created, instance, **kwargs):
    if created:
        profile = Profile(user=instance)
        profile.save()

这工作得很好,只要我选择一个account在管理,但如果我这样做,我会得到一个IntegrityError例外,告诉我, duplicate key value violates unique constraint "app_profile_user_id_key" DETAIL: Key (user_id)=(15) already exists.

显然,在线管理员试图创建一个profile实例本身,但我post_save信号处理,当时已经创造了它。

如何解决这个问题,同时保持所有的以下要求?

  1. 无论新用户是如何创建的,总是会有一个profile模型链接到它,以及之后。
  2. 如果用户选择的account用户创建过程中的管理,这个account将在新的设置profile模型之后。 如果没有,该字段为null.

环境:Django的1.5,Python 2.7版

相关的问题:

  • 创建扩展的用户简档 (类似的症状,但原因被证明是不同的一个)

Answer 1:

可以通过设置来避免这个问题primary_key=TrueOneToOneField在指向User模型,因为你已经找到了自己。

这个作品的原因似乎是相当简单的。

当您尝试创建一个模型实例,并设置pk保存之前手动,Django将尝试找到与数据库中的记录pk和更新它,而不是盲目地尝试创建一个新的。 如果不存在,它会创建如预期的新纪录。

当您设置OneToOneField作为主键,Django管理设置这一领域的相关User模型的ID,这意味着pk已经被设置和Django会尝试先找到现有的记录。

这与发生了什么OneToOneField设置主键:

  1. Django管理创建新的User实例,没有id
  2. Django管理节省了User实例。
    1. 因为pk (在这种情况下, id )没有设置,Django的尝试创建一个新的记录。
    2. 新的记录的id是由数据库自动设置。
    3. post_save钩创建一个新的Profile为该实例User实例。
  3. Django管理创建新的Profile的实例,其user设置为用户的id
  4. Django管理保存Profile实例。
    1. 因为pk (在这种情况下user )已被设置,则Django尝试获取与现有记录pk
    2. Django的发现现有的记录,并对其进行更新。

如果不设置主键明确,Django的,而不是补充说,使用数据库的字段auto_increment功能:数据库设置pk ,以不存在的一个最大的价值。 这意味着,除非你手动设置,因此Django将始终尝试插入一个新的记录,导致与在唯一性约束冲突的领域将实际留空OneToOneField

这是什么原因造成了原来的问题:

  1. Django管理创建新的User实例,没有id
  2. Django管理节省了User情况下, post_save钩创建一个新的Profile作为之前的实例。
  3. Django管理创建新的Profile的实例,没有id (自动添加的pk场)。
  4. Django管理保存Profile实例。
    1. 因为pk (在这种情况下, id )没有设置,Django的尝试创建一个新的记录。
    2. 该数据库举报违反上表的唯一性约束的user领域。
    3. Django的抛出异常。 你不会去太空今天。


Answer 2:

这似乎是设置primary_key=TrueOneToOneField连接配置文件模式向User模式修复此问题。 不过,我不认为我明白所有的意义,以及为什么它帮助。

在这里我要离开这个作为提示,但如果这是最好的解决方案,有人能拿出一个良好的书面解释,我会给予好评/接受,并可能删除我的。



文章来源: Creating a profile model with both an InlineAdmin and a post_save signal in Django