扩展 Django 用户模型 - 表单填充错误
Extending Django User model - form population errors
我正在使用 Player class 扩展 Django (v1.9) 的内置用户模型,以添加一些额外的属性。
class Player(models.Model):
TIMEZONES=()
user = models.OneToOneField(User, on_delete=models.CASCADE)
... (player-specific properties here)
time_zone = models.CharField(max_length=255, choices=PRETTY_TIMEZONE_CHOICES, blank=True, null=True,)
从 Django 管理面板创建用户时,我并不总是需要创建玩家,所以有时只创建用户。因此,玩家 ID 和用户 ID 并不完全匹配。事实证明,这会在填充链接到 Player 的模型的 ModelForms 时导致问题,例如:
class City(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE)
name = models.CharField(max_length = 100)
x_coord = models.SmallIntegerField()
y_coord = models.SmallIntegerField()
region = models.CharField(max_length = 100)
def __unicode__(self):
return str(self.player) + "-" + str(self.name)
class Meta:
db_table = 'cities'
class CityForm(ModelForm):
class Meta:
model = City
fields = (
'name',
'player',
'x_coord',
'y_coord',
'region')
创建新城市时使用此modelForm。当用户 ID 和玩家 ID 匹配时,没有问题,玩家 ID 会填充到表单中并且城市已成功创建。当User ID和Player ID不同时,表单中不填充player ID,表单验证失败,城市创建失败。
我从 request.user 获取玩家 ID 没问题,我可以在获取 POST 数据后验证之前修复玩家 ID。我还添加了一个 post-save 挂钩,以便始终创建 Player,因此 ID 将始终匹配。但似乎 应该 首先填充玩家 ID,因为用户数据是可访问的并且是一对一的关系。
我在这里错过了什么?
您缺少的是,当您实例化 ModelForm 以创建与某个现有对象相关的新行时,Django 无法知道相关对象的 ID。你需要以某种方式告诉它。
一种方法是,当您显示表单以响应 GET 时,使用表单构造函数的 initial
参数:
myform = MyModelFormClass(None, initial={ 'myfkfield': myrelatedobject.pk })
现在表单 class 知道在呈现表单时要预填什么值,并且在发布表单时,该字段将与它一起发布。
另一种方法是完全省略表单中的关系字段,然后在保存之前填写它,方法是使用表单保存方法的 commit
参数:
myform = MyModelFormClass(request.POST)
# this causes form values to be filled into the instance without actually
# writing to the database yet.
myinstance = myform.save(commit=False)
myinstance.myfkfield = myrelatedobject
# now really write to database
myinstance.save()
请注意,这将用于插入。对于更新,您需要将现有对象提供给您的模型构造函数,如下所示:
myinstance = MyModel.objects.get(pk=self.kwargs.pk)
myform = MyModelFormClass(request.POST, instance=myinstance)
没有实例,ModelForm 不知道它在数据库中更新的是哪一行。你会认为这些都存在于 HTML 中,所以没有必要……但这不是 Django 的工作方式。您需要从数据库中获取现有的对象,并将其与 request.POST
数据一起传递给 ModelForm 构造函数。然后,当您调用 myform.save()
时,它将验证表单,将其数据与现有对象合并,并保存该对象。使用 commit=False
会导致这三个步骤中的最后一步被推迟,这样您就可以在实际保存更新的实例之前对其进行任何调整或检查。
我正在使用 Player class 扩展 Django (v1.9) 的内置用户模型,以添加一些额外的属性。
class Player(models.Model):
TIMEZONES=()
user = models.OneToOneField(User, on_delete=models.CASCADE)
... (player-specific properties here)
time_zone = models.CharField(max_length=255, choices=PRETTY_TIMEZONE_CHOICES, blank=True, null=True,)
从 Django 管理面板创建用户时,我并不总是需要创建玩家,所以有时只创建用户。因此,玩家 ID 和用户 ID 并不完全匹配。事实证明,这会在填充链接到 Player 的模型的 ModelForms 时导致问题,例如:
class City(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE)
name = models.CharField(max_length = 100)
x_coord = models.SmallIntegerField()
y_coord = models.SmallIntegerField()
region = models.CharField(max_length = 100)
def __unicode__(self):
return str(self.player) + "-" + str(self.name)
class Meta:
db_table = 'cities'
class CityForm(ModelForm):
class Meta:
model = City
fields = (
'name',
'player',
'x_coord',
'y_coord',
'region')
创建新城市时使用此modelForm。当用户 ID 和玩家 ID 匹配时,没有问题,玩家 ID 会填充到表单中并且城市已成功创建。当User ID和Player ID不同时,表单中不填充player ID,表单验证失败,城市创建失败。
我从 request.user 获取玩家 ID 没问题,我可以在获取 POST 数据后验证之前修复玩家 ID。我还添加了一个 post-save 挂钩,以便始终创建 Player,因此 ID 将始终匹配。但似乎 应该 首先填充玩家 ID,因为用户数据是可访问的并且是一对一的关系。
我在这里错过了什么?
您缺少的是,当您实例化 ModelForm 以创建与某个现有对象相关的新行时,Django 无法知道相关对象的 ID。你需要以某种方式告诉它。
一种方法是,当您显示表单以响应 GET 时,使用表单构造函数的 initial
参数:
myform = MyModelFormClass(None, initial={ 'myfkfield': myrelatedobject.pk })
现在表单 class 知道在呈现表单时要预填什么值,并且在发布表单时,该字段将与它一起发布。
另一种方法是完全省略表单中的关系字段,然后在保存之前填写它,方法是使用表单保存方法的 commit
参数:
myform = MyModelFormClass(request.POST)
# this causes form values to be filled into the instance without actually
# writing to the database yet.
myinstance = myform.save(commit=False)
myinstance.myfkfield = myrelatedobject
# now really write to database
myinstance.save()
请注意,这将用于插入。对于更新,您需要将现有对象提供给您的模型构造函数,如下所示:
myinstance = MyModel.objects.get(pk=self.kwargs.pk)
myform = MyModelFormClass(request.POST, instance=myinstance)
没有实例,ModelForm 不知道它在数据库中更新的是哪一行。你会认为这些都存在于 HTML 中,所以没有必要……但这不是 Django 的工作方式。您需要从数据库中获取现有的对象,并将其与 request.POST
数据一起传递给 ModelForm 构造函数。然后,当您调用 myform.save()
时,它将验证表单,将其数据与现有对象合并,并保存该对象。使用 commit=False
会导致这三个步骤中的最后一步被推迟,这样您就可以在实际保存更新的实例之前对其进行任何调整或检查。