Hen*_*ger 11 python django django-signals django-models django-admin
我创建了一个"配置文件"模型(与用户模型具有一对一的关系),如扩展现有用户模型所述.配置文件模型与另一个模型具有可选的多对一关系:
class Profile(models.Model):
user = models.OneToOneField(User, primary_key=True)
account = models.ForeignKey(Account, blank=True, null=True, on_delete=models.SET_NULL)
Run Code Online (Sandbox Code Playgroud)
正如那里记录的那样,我还创建了一个内联管理员:
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
Run Code Online (Sandbox Code Playgroud)
现在,如果account在创建用户时未在管理员中选择a ,则不会创建配置文件模型.所以我再次连接到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()
Run Code Online (Sandbox Code Playgroud)
只要我没有account在管理员中选择一个,这个工作正常,但如果我这样做,我会得到一个IntegrityError例外,告诉我duplicate key value violates unique constraint "app_profile_user_id_key" DETAIL: Key (user_id)=(15) already exists.
显然,内联管理员尝试创建profile实例本身,但我的post_save信号处理程序已经在那时创建了它.
如何解决此问题,同时保持以下所有要求?
profile模型链接到它.account在创建用户期间在管理员中选择了一个,则之后account将在新profile模型上设置.如果没有,该领域是null.环境:Django 1.5,Python 2.7
相关问题:
Ala*_*lum 23
可以通过设置来避免这个问题primary_key=True上OneToOneField的指点User模式,因为你已经找到了自己.
这个工作的原因似乎相当简单.
当您尝试创建模型实例并pk在保存之前手动设置时,Django将尝试使用该实例在数据库中查找记录pk并更新它而不是盲目地尝试创建新记录.如果不存在,则按预期创建新记录.
当您将OneToOneField主键设置为主键并且Django Admin将该字段设置为相关User模型的ID时,这意味着pk已经设置了该字段,Django将首先尝试查找现有记录.
这是OneToOneField设置为主键时发生的情况:
User实例,没有id.User实例.
pk(在这种情况下id)没有设置,Django尝试创建一个新记录.id由数据库自动设置.post_save钩创建一个新的Profile用于该实例User的实例.Profile实例,并将其user设置为用户的实例id.Profile实例.
pk(在这种情况下user)已经设置,Django尝试用它来获取现有记录pk.如果没有显式设置主键,Django会添加一个使用数据库auto_increment功能的字段:数据库将其设置为pk不存在的下一个最大值.这意味着该字段实际上将留空,除非您手动设置它,因此Django将始终尝试插入新记录,从而导致与唯一性约束冲突OneToOneField.
这是导致原始问题的原因:
User实例,没有id.User实例,post_save钩子Profile像以前一样创建一个新实例.Profile实例,没有id(自动添加的pk字段).Profile实例.
pk(在这种情况下id)没有设置,Django尝试创建一个新记录.user字段唯一性约束.