29

in my Django (1.2) project, I want to prepopulate a field in a modelform, but my new value is ignored. This is the snippet:

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'

I also tried with self.base_fields, but no effect: there is always the database-value displaying in the form. Any ideas?

Serjik
  • 10,543
  • 8
  • 61
  • 70
schneck
  • 10,556
  • 11
  • 49
  • 74

2 Answers2

53

Try this:

def __init__(self, *args, **kwargs):
    initial = kwargs.get('initial', {})
    initial['material'] = 'Test'
    kwargs['initial'] = initial
    super(ArtefactForm, self).__init__(*args, **kwargs)
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
19

Old question but adding a descriptive answer as I believe it would be helpful for some new developer.

I also tried with self.base_fields, but no effect: there is always the database-value displaying in the form. Any ideas?

If a form is "initialized" form, either:-

  1. using initial argument (eg. YourModelFrom(initial={'filed1': variable}) — generally the case, when you want to pass dynamically calculated initial values for some fields). Reference Setting Initial Values

    or

  2. using instance argument (eg. YourModelFrom(instance=model_object) — usually the case, when you wants to update an existing model instance object) . References read ModelFrom's save() method

    Note:
    1 `ModelFrom` class inherits `BaseModelFrom` class. The `BaseModelFrom` class inherits `BaseForm` class.
    2 The argument instance is added in `BaseModelFrom` class constructor, when we assign a model class instance object to instance argument (hence instance is not None) then `BaseModelFrom` constructor calls model_to_dict() and updates initial argument before to call super class constructor. Check def __init__ in BaseModelFrom class

Then assigning initial value to a field explicitly (as shown in OP's code in question) don't effect, this is due to the way _clean_fields method is written in BaseFrom class.

Code Snip:

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:

According to code line initial = self.initial.get(name, field.initial), if initial value for field is given in initial dict then value assigned to field.initial is not used.

[ANSWER]:

Although, @Daniel answer is perfectly correct but one can also like another way to achieve same effect is using self.initial:

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

Give it a try!!

self.initial is nothing but it the dict we pass in argument. Check code __init__ in BaseForm:

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

Note: I didn't find any documentation related to use initial attribute, I just explored the base code and used it.

Edit: the behavior reported in Question is also documented in Django Model

Dynamic initial values

Form.initial

Note that if a Field defines initial and you include initial when instantiating the Form, then the latter initial will have precedence. In this example, initial is provided both at the field level and at the form instance level, and the latter gets precedence:

>>> 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: Btw, point 2 in my answers tell difference between initial and instance argument. Their is one more key-value argument data - values in that triggers form validations. read this Difference between Django Form 'initial' and 'bound data'?.

Community
  • 1
  • 1
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • **Note:** In bound form, `data` (the query dict) will have precedence over initialized field's value. Check code: [`def value(self)`](https://github.com/django/django/blob/master/django/forms/forms.py#L610) and method [`def bound_data(self, data, initial)`](https://github.com/django/django/blob/master/django/forms/fields.py#L165) -- this will help you if you have a value for a field os dynamically calculated and the filed is ['disabled'](http://stackoverflow.com/a/1424453/1673391). Let me know if anyone need help on this I will update this answer! – Grijesh Chauhan Dec 06 '14 at 13:13
  • 1
    self.initial[field] did the trick here. Thank you very much. – Raphael Fernandes Mar 15 '22 at 21:20