【问题标题】:How to set initial values for a ModelForm when instance is also given当还给出实例时如何为 ModelForm 设置初始值
【发布时间】:2013-08-17 17:50:53
【问题描述】:

看起来如果给 ModelForm 一个实例,它会忽略您为 initial 提供的任何值,而是将其设置为实例的值——即使该实例是空模型记录。

有没有办法用实例创建表单设置初始数据?

我需要它,因为我正在保存相关记录,但它们似乎无法正确保存,除非在创建时为 ModelForm 提供了一个实例。

我确信这个问题的答案很简单,我只是遗漏了一些明显的东西。

以下是相关代码:

在视图中:

form = form_class(person=person, conference=conference, initial=initial, instance=registration)

form_class 是RegistrationForm,然后在注册表单中:

class RegisterForm(forms.ModelForm):
    ... fields here ...

    def __init__(self, *args, **kwargs):
        ... other code ...
        self.person = kwargs.pop('person')
        super(RegisterForm, self).__init__(*args, **kwargs)
        for key, in self.fields.keys():
            if hasattr(self.person, key):
                self.fields[k].initial = getattr(self.person, key)

然后当我调用该字段时,相关字段为空。

【问题讨论】:

标签: django django-forms django-1.4


【解决方案1】:

在我看来,您可能正在寻找绑定表格。不完全确定,我正在尝试取消选择类似的问题:

Django 表单可以用两个控制这种事情的参数来实例化。据我了解:

form = MyForm(initial={...}, data={...}, ...)

initial 将为字段设置 可能 值——就像设置查询集一样——data 将设置表单的实际(或选定)值并创建绑定表单.也许这就是你想要的。另一个你可能会感兴趣的切线点是考虑工厂方法而不是构造函数,我认为语法更自然:

class MyForm(forms.ModelForm):

    ...

    @staticmethod
    def makeBoundForm(user):
        myObjSet = MyObject.objects.filter(some_attr__user=user)
        if len(myObjSet) is not 0:
            data = {'myObject': myObjSet[0]}
        else:
            raise ValueError()
        initial = {'myObject': myObjSet}
        return MyForm(initial=initial, data=data)

【讨论】:

  • 不完全是。在这种情况下,更像是我试图用另一种模型的字段为一种模型预填充一些字段。例如,根据该用户的现有地址填写订单的地址字段。
  • 我明白了,我认为您仍然可以应用我提出的基本观点。这个page 描述了如何以一般方式将模型转换为字典(这不是一个真正的难题,但应该节省一些时间)一旦你有了字典,你可以删除任何你不想设置的字段并将其提供为data 参数。我想这是 instance 的替代方案,它可以更好地控制所使用的表单初始化数据。
【解决方案2】:

您还可以在初始化类时将额外的变量传递给类。然后,您传递的值可以覆盖初始数据或 POST 数据。

class RegisterForm(forms.ModelForm):
    ... fields here ...

    def __init__(self, person, conference, *args, **kwargs):
        ... other code ...
        super(RegisterForm, self).__init__(*args, **kwargs)
        self.fields['person'] = person
        self.fields['conference'] = conference

form = RegisterForm(person, conference, initial=initial, instance=registration)

【讨论】:

  • 除非我读错了,否则这会用模型中的值替换字段。我想不出你真正想要这样做的用例。
  • 初始数据和实例数据将传递给super(RegisterForm, self).__init__(*args, **kwargs),然后从视图(或创建表单实例的任何位置)显式传入的值personconference 将覆盖@ 987654325@和instance
  • 阅读您的代码。您将self.fields['person'] 设置为person 中包含的值。 self.fields['person'] 是一个字段; person 不是。也许你的意思是self.fields['person'].initial = person?因为我想不出您想用记录替换表单的某个字段的任何情况。
【解决方案3】:

经过一番谷歌搜索后发现了这一点。

你必须在调用super之前设置初始值

因此,我不必循环遍历self.fields.keys(),而是输入我想要的字段列表并循环遍历:

class RegisterForm(forms.ModelForm):
    ... fields here ...
    initial_fields = ['first_name', 'last_name', ... ]

    def __init__(self, *args, **kwargs):
        ... other code ...
        self.person = kwargs.pop('person')
        for key in self.initial_fields:
            if hasattr(self.person, key):
                self.fields[k].initial = getattr(self.person, key)
        super(RegisterForm, self).__init__(*args, **kwargs)

@Daria 正确地指出,在调用 super 之前您没有 self.fields。我很确定这会奏效:

class RegisterForm(forms.ModelForm):
    ... fields here ...
    initial_fields = ['first_name', 'last_name', ... ]

    def __init__(self, *args, **kwargs):
        ... other code ...
        initial = kwargs.pop('initial', {})
        self.person = kwargs.pop('person')
        for key in self.initial_fields:
            if hasattr(self.person, key):
                initial[key] = initial.get(key) or getattr(self.person, key)
        kwargs['initial'] = initial
        super(RegisterForm, self).__init__(*args, **kwargs)

在这个版本中,我们使用initial 参数来传递值。它还被编写成如果我们已经为该字段提供了初始值,我们不会覆盖它。

【讨论】:

  • 但是在调用超级构造函数之前,你没有self.fields
  • 嗯,你是对的。不知道我是如何完成这项工作的……大约半年前。我得看看我是否能找到相关的代码以及它最终是如何实现的。
  • for key, in self.initial_fields: 不是有效的 Python。如果我没记错,请删除, 或添加下划线, _
猜你喜欢
  • 2010-11-06
  • 1970-01-01
  • 2018-08-21
  • 2015-10-25
  • 2010-12-14
  • 2020-11-14
  • 2019-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多