问题描述
我创建了一个个人资料模型(与用户模型具有1对1的关系),如。配置文件模型与另一个模型具有可选的多对一关系:
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. So I connect to the post_save signal, again just following the documentation:
@receiver(post_save, sender=User)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
只要我没有在管理员中选择一个帐户
,只要我执行,就可以正常工作,但如果我这样做,我会得到一个code> IntegrityError 异常,告诉我重复键值违反唯一约束app_profile_user_id_keyDETAIL:Key(user_id)=(15)已经存在。
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.
显然,内联管理员尝试创建个人资料
实例本身,但我的 post_save
信号处理程序当时已经创建了它。
Apparently, the inline admin tries to creates the profile
instance itself, but my post_save
signal handler has already created it at that time.
如何解决这个问题,同时保持以下所有要求?
How do I fix this problem, while keeping all of the following requirements?
- 无论新用户如何创建,将始终有一个
个人资料
模型链接到 - 如果用户在创建用户期间在管理员中选择了一个
帐户
,这个帐户
将在新的
配置文件
之后设置。如果没有,该字段是null。
- No matter how the new user is created, there will always be a
profile
model linking to it as well afterwards. - If the user selects an
account
in the admin during user creation, thisaccount
will be set on the newprofile
model afterwards. If not, the field isnull.
环境:Django 1.5,Python 2.7
Environment: Django 1.5, Python 2.7
相关问题:
- (类似的症状,但原因是另一个)
- Creating a extended user profile (similar symptoms, but the cause turned out to be a different one)
推荐答案
可以通过将 primary_key = True
OneToOneField
指向用户
模型,就像您已经弄清楚一样。
The problem can be avoided by setting primary_key=True
on the OneToOneField
pointing at the User
model, as you have figured out yourself.
这个工作原理似乎很简单。
The reason that this works seems to be rather simple.
当您尝试创建一个模型实例并设置 pk
手动保存之前,Django将尝试使用 pk
在数据库中查找记录,并进行更新,而不是盲目地尝试创建一个新的。如果没有,则按预期创建新记录。
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. If none exists, it creates the new record as expected.
当您将 OneToOneField
设置为主键时, Django Admin将该字段设置为相关的用户
模型的ID,这意味着 pk
已经设置,Django会尝试找到现有记录。
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
设置为主键的情况:
This is what happens with the OneToOneField
set as primary key:
- Django管理员创建新的
用户
实例,没有id
。
- Django Admin保存
用户
实例。
- Django Admin creates the new
User
instance, with noid
. - Django Admin saves the
User
instance.
- 因为
pk
(在这种情况下id
)是未设置,Django尝试创建新记录。 - 新记录的
id
由数据库自动设置。 -
post_save
钩子为该用户创建一个新的
实例。配置文件
实例
- Because the
pk
(in this caseid
) is not set, Django attempts to create a new record. - The new record's
id
is set automatically by the database. - The
post_save
hook creates a newProfile
instance for thatUser
instance.
个人资料
实例,其用户
设置为用户的 id
。配置文件
实例。 Profile
instance, with its user
set to the user's id
.Profile
instance.- 因为
pk
(在这种情况下user
)是已经设置好,Django尝试使用pk
获取现有记录。 - Django查找现有记录并对其进行更新。 >
- Because the
pk
(in this caseuser
) is already set, Django attempts to fetch an existing record with thatpk
. - Django finds the existing record and updates it.
如果没有显式设置主键,Django会添加一个使用数据库的 auto_increment
功能:数据库将 pk
设置为不存在的下一个最大值。这意味着该字段实际上将保留为空白,除非您手动设置,否则Django将始终尝试插入新记录,导致与 OneToOneField
。
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. 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管理员创建新的
用户
实例,没有id
。 - Django Admin保存
用户
实例,post_save
钩子创建一个新的配置文件
实例如前所述。 - Django管理员创建新的
个人资料
实例,没有id
($自动添加pk
字段)。 - Django Admin保存
个人资料
实例。
- Django Admin creates the new
User
instance, with noid
. - Django Admin saves the
User
instance, thepost_save
hook creating a newProfile
instance as before. - Django Admin creates the new
Profile
instance, with noid
(the automatically addedpk
field). - Django Admin saves the
Profile
instance.
- 因为
pk
(在这种情况下id
)是没有设置,Django尝试创建一个新的记录。 - 数据库报告了对
用户
字段的表的唯一性约束的违反。 - Django会抛出异常。你今天不会去太空。
- Because the
pk
(in this caseid
) is not set, Django attempts to create a new record. - The database reports a violation of the table's uniqueness-constraint on the
user
field. - Django throws an Exception. You will not go to space today.
这篇关于在Django中使用InlineAdmin和post_save信号创建配置文件模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!