【问题标题】:Extending Django User model - form population errors扩展 Django 用户模型 - 表单填充错误
【发布时间】:2017-05-15 18:10:34
【问题描述】:

我正在使用 Player 类扩展 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 被填充到表单中,并且城市创建成功。当用户ID和玩家ID不同时,表单中没有填写玩家ID,表单验证失败,城市创建失败。

我从 request.user 获取玩家 ID 没有问题,我可以在获取 POST 数据后验证之前修复玩家 ID。我还添加了一个保存后挂钩,以便始终创建 Player,因此 ID 将始终匹配。但似乎表单应该首先填充玩家 ID,因为用户数据是可访问的,并且是一对一的关系。

我在这里错过了什么?

【问题讨论】:

    标签: python django django-forms modelform


    【解决方案1】:

    您缺少的是,当您实例化 ModelForm 以创建与某个现有对象相关的新行时,Django 无法知道相关对象的 id。你需要以某种方式告诉它。

    一种方法是,当您显示表单以响应 GET 时,使用表单构造函数的 initial 参数:

    myform = MyModelFormClass(None, initial={ 'myfkfield': myrelatedobject.pk })
    

    现在表单类知道在呈现表单时要预先填写什么值,并且在发布表单时,该字段将与它一起发布。

    另一种方法是完全省略表单中的关系字段,然后在保存之前稍后填写,方法是使用表单保存方法的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 会导致这三个步骤中的最后一个步骤被推迟,以便您可以在实际保存之前对更新的实例进行任何调整或检查。

    【讨论】:

    • 我没有对 ID 做任何假设,恰恰相反,我认为玩家 ID 会在 modelForm 中正确填充是理所当然的。问题不在于多个城市 ID,而在于:当用户创建一个新城市时,用户的玩家 ID 不会填充到城市创建表单中。
    • 啊,我明白了,你试图解决这个问题,我会更新我的答案
    • 好的,所以我已经改变了我的答案,希望这会有所帮助。如果你还是不明白,那我可以试着把它弄清楚。
    猜你喜欢
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 2016-10-12
    • 2011-02-28
    • 2013-12-06
    • 2018-01-21
    相关资源
    最近更新 更多