在ModelForm中覆盖初始值

29 投票
2 回答
20084 浏览
提问于 2025-04-15 23:39

在我的Django(1.2)项目中,我想在一个模型表单里预先填充一个字段,但我新的值总是被忽略。以下是相关代码:

class ArtefactForm(ModelForm):
    material = CharField(widget=AutoCompleteWidget('material', force_selection=False))

    def __init__(self, *args, **kwargs):
        super(ArtefactForm, self).__init__(*args, **kwargs)
        self.fields['material'].initial = 'Test'

我也试过用 self.base_fields,但是没有效果:表单里总是显示数据库里的值。有没有什么想法?

2 个回答

19

这个问题虽然老了,但我觉得加个详细的回答会对一些新开发者有帮助。

我也试过用 self.base_fields,但没效果:表单里总是显示数据库里的值。有什么想法吗?

如果一个表单是“初始化”的,可能有两种情况:

  1. 使用 initial 参数(比如 YourModelFrom(initial={'filed1': variable}) — 通常在你想为某些字段传递动态计算的初始值时使用)。参考 设置初始值

    或者

  2. 使用 instance 参数(比如 YourModelFrom(instance=model_object) — 通常在你想更新一个已有的模型实例对象时使用)。参考 save() 方法

    注意:
    1 `ModelFrom` 类继承自 `BaseModelFrom` 类。
    2 当我们将模型类实例对象分配给 instance 参数时(因此 instance is not None),`BaseModelFrom` 的构造函数会调用 model_to_dict() 并在调用父类构造函数之前更新 initial 参数。查看 def __init__ 在 BaseModelFrom 类中

然后,明确给字段赋初始值(就像问题中 OP 的代码那样)没有效果,这是因为 _clean_fields 方法在 BaseFrom 类中的写法。

代码片段:

def _clean_fields(self):
    for name, field in self.fields.items():
        value = field.widget.value_from_datadict(
                self.data, self.files, self.add_prefix(name))
        try:
            if isinstance(field, FileField):
                initial = self.initial.get(name, field.initial) # <- Check
                value = field.clean(value, initial)
            else:

根据代码行 initial = self.initial.get(name, field.initial),如果在 initial 字典中给了字段的初始值,那么分配给 field.initial 的值将 被使用。

[回答]

虽然 @Daniel 的回答是完全正确的,但还有另一种方法可以达到相同的效果,就是使用 self.initial

def __init__(self, *args, **kwargs):
    super(ArtefactForm, self).__init__(*args, **kwargs)
    self.initial['material'] = 'Test'

试试看吧!!

self.initial 其实就是我们传入的字典。查看代码 __init__BaseForm 中:

class BaseForm(object):
    def __init__(........
                 ........):
        :
        self.prefix = prefix
        self.initial = initial or {}  # <-- Notice 
        self.error_class = error_class
        :

注意:我没有找到关于使用 initial 属性的文档,我只是探索了基础代码并使用了它。

编辑:问题中报告的行为在 Django 模型中也有文档说明。

动态初始值

Form.initial

注意,如果一个 Field 定义了 initial,而你在实例化表单时也包含了 initial,那么后者的 initial 将优先使用。在这个例子中,initial 在字段级别和表单实例级别都有提供,而后者优先:

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial='class')
...     url = forms.URLField()
...     comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" />
<tr><th>Url:</th><td><input type="url" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>

PS:顺便说一下,我回答中的第 2 点说明了 initialinstance 参数之间的区别。还有一个关键的键值参数 data - 这个值会触发表单验证。阅读这个 Django 表单 'initial' 和 '绑定数据' 的区别?

53

试试这个:

def __init__(self, *args, **kwargs):
    initial = kwargs.get('initial', {})
    initial['material'] = 'Test'
    kwargs['initial'] = initial
    super(ArtefactForm, self).__init__(*args, **kwargs)

撰写回答