143

I am trying to set the field to a certain value after the form is initialized.

For example, I have the following class.

class CustomForm(forms.Form):
    Email = forms.EmailField(min_length=1, max_length=200)

In the view, I want to be able to do something like this:

form = CustomForm()
form["Email"] = GetEmailString()

return HttpResponse(t.render(c))
Eldila
  • 15,426
  • 23
  • 58
  • 62

10 Answers10

170

Since you're not passing in POST data, I'll assume that what you are trying to do is set an initial value that will be displayed in the form. The way you do this is with the initial keyword.

form = CustomForm(initial={'Email': GetEmailString()})

See the Django Form docs for more explanation.

If you are trying to change a value after the form was submitted, you can use something like:

if form.is_valid():
    form.cleaned_data['Email'] = GetEmailString()

Check the referenced docs above for more on using cleaned_data

Grant
  • 2,838
  • 1
  • 19
  • 17
  • 2
    This confirms what I knew, but my `ModelChoiceField` still is giving invalid_choice when I give it an `initial` value :( – markwalker_ Oct 05 '12 at 07:13
  • Yes, this is my problem, too. And changing form.data['Email'] is not possible. – GergelyPolonkai May 26 '15 at 12:30
  • 3
    i changed mine to `form.data['Email'] = GetEmailString()` and it works – psychok7 Oct 29 '15 at 13:47
  • 1
    I need to set it *between* CustomForm(request.POST) and .is_valid(), so I can't use either initial in the constructor, nor the cleaned_data. I tried using form.data, but it's immutable (Django 1.11). – Teekin Aug 02 '17 at 13:04
  • 4
    The correct solution to this question, should be: `form.fields['Email'].initial = GetEmailString()` The `form.fields['keyword'].initial` gives you access to initialize your values even after having instanciated your form – vctrd Jan 07 '19 at 21:02
  • 1
    The answer is wrong when trying to change a value after submission: bounded forms cannot be changed, you have to create a new one (with new values). See https://docs.djangoproject.com/en/3.0/ref/forms/api/#django.forms.Form.is_bound – Edouard Thiel Apr 15 '20 at 13:04
105

If you've already initialized the form, you can use the initial property of the field. For example,

form = CustomForm()
form.fields["Email"].initial = GetEmailString()
Josh
  • 12,896
  • 4
  • 48
  • 49
  • 1
    Also works great in a for loop with a formset, just use the logic "for form in formset" and you can set choices and initial data as seen above. – radtek Aug 26 '14 at 20:44
  • 6
    Is there a different way to do this is Django 1.7/Python 3.4? This isn't working for me – Jeremy Jan 12 '15 at 22:01
  • 2
    @JeremyCraigMartinez: No... Is the form you are trying a bound form (with GET/POST data passed in)? The docs say _This is why initial values are only displayed for unbound forms. For bound forms, the HTML output will use the bound data._, see [here](https://docs.djangoproject.com/en/1.8/ref/forms/fields/#initial) – Markus Aug 11 '15 at 09:26
  • 13
    the correct way is form.initial["Email"] = GetEmailString() – Elio Scordo Jan 13 '16 at 18:37
19

If you want to do it within the form's __init__ method for some reason, you can manipulate the initial dict:

class MyForm(forms.Form):
    my_field = forms.CharField(max_length=255)

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.initial['my_field'] = 'Initial value'
seddonym
  • 16,304
  • 6
  • 66
  • 71
13

Something like Nigel Cohen's would work if you were adding data to a copy of the collected set of form data:

form = FormType(request.POST)
if request.method == "POST":
    formcopy = form(request.POST.copy())
    formcopy.data['Email'] = GetEmailString()
Milo P
  • 1,377
  • 2
  • 31
  • 40
  • 2
    I'm not a big fan of overriding the raw data. If you absolutely have to do this, you should probably do `data[form.add_prefix('Email')]` to account for cases where a prefix is set. – Josh Aug 22 '14 at 14:27
  • 3
    Is there a different way to do this is Django 1.7/Python 3.4? This isn't working for me – Jeremy Jan 12 '15 at 21:56
  • 2
    The 3rd line can be a little bit shorter if no need to keep original form: `form.data = form.data.copy()` – ZZY Feb 10 '15 at 15:43
  • 1
    @Josh, here is one scenario of the need: use a form to validate input -> processing with the input -> processing done -> erase/modify several fields of the form and render to HTML as response. Example, there are 'name', 'email', 'content' fields in a form, I want to keep 'name' and 'email', but erase 'content'. So that users don't need to input name/email again, if they want to submit another POST. Of course initialize a new form with the same name/email as initial value is one way, but I think erasing on the old form is simpler in some cases – ZZY Feb 10 '15 at 15:50
  • 1
    I'm not sure I fully understand. However, if I do, it sounds to me like you should, instead, be using `modelform_factory`. This way you can generate a Form class that doesn't have the fields you don't want. It's very dangerous to have a Form class that has fields that aren't rendered as the form object will still accept data for the non-rendered fields. An attacker could use this to their advantage. – Josh Feb 10 '15 at 17:54
10

If you have initialized the form like this

form = CustomForm()

then the correct way as of Jan 2019, is to use .initial to replace the data. This will replace the data in the intial dict that goes along with the form. It also works if you have initialized using some instance such as form = CustomForm(instance=instance)

To replace data in the form, you need to

form.initial['Email'] = GetEmailString()

Generalizing this it would be,

form.initial['field_name'] = new_value
Vineeth Sai
  • 3,389
  • 7
  • 23
  • 34
6

Just change your Form.data field:

class ChooseProjectForm(forms.Form):
    project = forms.ModelChoiceField(queryset=project_qs)
    my_projects = forms.BooleanField()

    def __init__(self, *args, **kwargs):
        super(ChooseProjectForm, self).__init__(*args, **kwargs)
        self.data = self.data.copy()  # IMPORTANT, self.data is immutable
        # any condition:
        if self.data.get('my_projects'):
            my_projects = self.fields['project'].queryset.filter(my=True)
            self.fields['project'].queryset = my_projects
            self.fields['project'].initial = my_projects.first().pk
            self.fields['project'].empty_label = None  # disable "-----"
            self.data.update(project=my_projects.first().pk)  # Update Form data
            self.fields['project'].widget = forms.HiddenInput()  # Hide if you want
ckarrie
  • 151
  • 2
  • 1
1

To throw yet another way into the mix: this works too, with a bit more modern notation. It just works around the fact that a QueryDict is immutable.

>>> the_form.data = {**f.data.dict(), 'some_field': 47}
>>> the_form['some_field'].as_widget()
'<input type="hidden" name="some_field" value="47"
        class="field-some_field" id="id_some_field">'
dr. Sybren
  • 829
  • 5
  • 18
1

in widget use 'value' attr. Example:

username = forms.CharField(
    required=False,
    widget=forms.TextInput(attrs={'readonly': True, 'value': 'CONSTANT_VALUE'}),
)
Lucas Vazquez
  • 1,456
  • 16
  • 20
0

In your case it seems that you are trying to create a new empty (unbound) form and set an initial value that will be displayed in this form.

In this case it is better to instantiate the form with the initial value (instead of trying to change the value after the form is initialized).

There are multiple options how to do this but the simplest one would be the one presented in Grant's answer. i.e.:

form = CustomForm(initial={'Email': GetEmailString()})
Jakub Holan
  • 303
  • 1
  • 8
-11

Another way to do this, if you have already initialised a form (with or without data), and you need to add further data before displaying it:

form = Form(request.POST.form)
form.data['Email'] = GetEmailString()
  • 9
    This fails for me. I have a line like the above in my form's __init__ method, and it raises "AttributeError: This QueryDict instance is immutable" – Jonathan Hartley Jun 16 '11 at 16:32
  • 1
    This code only works when the form has been initialised without any form data. As a result, an immutable object like request.GET or request.POST will not be bound, so you will get an empty dictionary (form.data) that can be changed without any errors. FYI I'm using Django 1.6.3 – potar Jun 15 '15 at 07:39
  • 1
    This will work if your "Content-Type" header is set to "multipart/form-data". It fails when you use a content-type of "application/x-www-form-urlencoded". I don't know why, but it was a ***** to debug. – teewuane Jun 23 '16 at 18:47