[英]Creating a profile model with both an InlineAdmin and a post_save signal in Django
I created a 'profile' model (with a 1-to-1 relationship to the User model) as described on Extending the existing user model . 我创建了一个“配置文件”模型(与用户模型具有一对一的关系),如扩展现有用户模型所述 。 The profile model has an optional many-to-one relationship to another model:
配置文件模型与另一个模型具有可选的多对一关系:
class Profile(models.Model):
user = models.OneToOneField(User, primary_key=True)
account = models.ForeignKey(Account, blank=True, null=True, on_delete=models.SET_NULL)
As documented there, I also created an inline admin: 正如那里记录的那样,我还创建了一个内联管理员:
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
Now if I don't select an account
in the admin when creating the user, the profile model won't be created. 现在,如果在创建用户时未在管理员中选择
account
,则不会创建配置文件模型。 So I connect to the post_save signal, again just following the documentation: 所以我再次连接到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()
This works fine as long as I do not select an account
in the admin, but if I do, I'll get an IntegrityError
exception, telling me that duplicate key value violates unique constraint "app_profile_user_id_key" DETAIL: Key (user_id)=(15) already exists.
只要我没有在管理员中选择一个
account
,这样就行得很好 ,但如果我这样做,我将收到一个IntegrityError
异常,告诉我duplicate key value violates unique constraint "app_profile_user_id_key" DETAIL: Key (user_id)=(15) already exists.
Apparently, the inline admin tries to creates the profile
instance itself, but my post_save
signal handler has already created it at that time. 显然,内联管理员尝试自己创建
profile
实例,但我的post_save
信号处理程序已经在那时创建了它。
How do I fix this problem, while keeping all of the following requirements? 如何解决此问题,同时保持以下所有要求?
profile
model linking to it as well afterwards. profile
模型。 account
in the admin during user creation, this account
will be set on the new profile
model afterwards. account
,则此account
将在之后的新profile
模型中设置。 If not, the field is null.
null.
Environment: Django 1.5, Python 2.7 环境:Django 1.5,Python 2.7
Related questions: 相关问题:
The problem can be avoided by setting primary_key=True
on the OneToOneField
pointing at the User
model, as you have figured out yourself. 通过在指向
User
模型的OneToOneField
上设置primary_key=True
可以避免此问题,因为您已经弄明白了。
The reason that this works seems to be rather simple. 这个工作的原因似乎相当简单。
When you try to create a model instance and set the pk
manually before saving it, Django will try to find a record in the database with that pk
and update it rather than blindly attempting to create a new one. 当您尝试创建模型实例并在保存之前手动设置
pk
时,Django将尝试使用该pk
在数据库中查找记录并更新它而不是盲目地尝试创建新记录。 If none exists, it creates the new record as expected. 如果不存在,则按预期创建新记录。
When you set the OneToOneField
as the primary key and Django Admin sets that field to the related User
model's ID, that means the pk
is already set and Django will attempt to find an existing record first. 当您将
OneToOneField
设置为主键并且Django Admin将该字段设置为相关User
模型的ID时,这意味着已经设置了pk
,Django将首先尝试查找现有记录。
This is what happens with the OneToOneField
set as primary key: 这是将
OneToOneField
设置为主键时发生的情况:
User
instance, with no id
. User
实例,没有id
。 User
instance. User
实例。
pk
(in this case id
) is not set, Django attempts to create a new record. pk
(在本例中为id
),Django会尝试创建新记录。 id
is set automatically by the database. id
由数据库自动设置。 post_save
hook creates a new Profile
instance for that User
instance. post_save
挂钩为该User
实例创建一个新的Profile
实例。 Profile
instance, with its user
set to the user's id
. Profile
实例,其user
设置为用户的id
。 Profile
instance. Profile
实例。
pk
(in this case user
) is already set, Django attempts to fetch an existing record with that pk
. pk
(在这种情况下是user
),Django尝试用该pk
获取现有记录。 If you don't set the primary key explicitly, Django instead adds a field that uses the database's auto_increment
functionality: the database sets the pk
to the next largest value that doesn't exist. 如果没有显式设置主键,Django会添加一个使用数据库
auto_increment
功能的auto_increment
:数据库将pk
设置为不存在的下一个最大值。 This means the field will actually be left blank unless you set it manually and Django will therefore always attempt to insert a new record, resulting in a conflict with the uniqueness-constraint on the OneToOneField
. 这意味着该字段实际上将保留为空,除非您手动设置它,因此Django将始终尝试插入新记录,从而导致与
OneToOneField
上的唯一性约束冲突。
This is what causes the original problem: 这是导致原始问题的原因:
User
instance, with no id
. User
实例,没有id
。 User
instance, the post_save
hook creating a new Profile
instance as before. User
实例, post_save
挂钩像以前一样创建一个新的Profile
实例。 Profile
instance, with no id
(the automatically added pk
field). Profile
实例,没有id
(自动添加的pk
字段)。 Profile
instance. Profile
实例。
pk
(in this case id
) is not set, Django attempts to create a new record. pk
(在本例中为id
),Django会尝试创建新记录。 user
field. user
字段的唯一性约束。 It seems like setting primary_key=True
on the OneToOneField
connecting the profile model to the User
model fixes this issue. 似乎在将配置文件模型连接到
User
模型的OneToOneField
上设置primary_key=True
可以解决此问题。 However, I don't think I understand all the implications of that and why it helps. 但是,我认为我不了解其中的所有含义及其有用的原因。
I'll leave this here as a hint, but if that's the best solution and someone could come up with a well-written explanation, I'd upvote/accept that and possibly delete mine. 我会把这个留在这里作为暗示,但如果这是最好的解决方案,有人可以提出一个写得很好的解释,我会投票/接受,并可能删除我的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.