55

When I have a valid Django form, I can access the data with form.cleaned_data. But how do I get at the data a user entered when the form is not valid i.e., form.is_valid is false.

I'm trying to access forms within a form set, so form.data seems to just give me a mess.

Rodrigo Guedes
  • 1,169
  • 2
  • 13
  • 27
Greg
  • 45,306
  • 89
  • 231
  • 297
  • 2
    Related ticket was fixed. Django 1.5 will not remove cleaned_data if form is not valid: https://code.djangoproject.com/ticket/5524 – guettli Aug 08 '12 at 06:56

7 Answers7

55

You can use

form.data['field_name']

This way you get the raw value assigned to the field.

Jiaaro
  • 74,485
  • 42
  • 169
  • 190
Dmitry Risenberg
  • 2,321
  • 2
  • 18
  • 22
  • However, it looks like this dictionary doesn't hold the raw data for any files assuming a file was submitted. For example, say there is a file field on your form called "logo." If you submit the form without specifying a logo, then form.data['logo'] = "". However, if you specify a logo, form.data['logo'] doesn't exist. Anyone know where it goes? – Josh Jun 03 '12 at 17:24
  • Silly Josh, it goes in form.files - amazing what you can discover with iPython introspection and actually looking at the field names :). – Josh Jun 03 '12 at 17:26
  • how would you do that if the field is empty. it gives an error if the field is empty – Fuad Jul 09 '15 at 19:57
  • 2
    @Fuad `form.data.get('field_name', None)` – Escher Apr 26 '17 at 10:11
17

See http://docs.djangoproject.com/en/dev/ref/forms/validation/#ref-forms-validation

Secondly, once we have decided that the combined data in the two fields we are considering aren't valid, we must remember to remove them from the cleaned_data.

In fact, Django will currently completely wipe out the cleaned_data dictionary if there are any errors in the form. However, this behaviour may change in the future, so it's not a bad idea to clean up after yourself in the first place.

The original data is always available in request.POST.


A Comment suggests that the point is to do something that sounds like more sophisticated field-level validation.

Each field is given the unvalidated data, and either returns the valid data or raises an exception.

In each field, any kind of validation can be done on the original contents.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • I was hoping there was a better way since that has the raw, mangled input names that the formset made up, e.g., form-1-my_input. So I'd have to do some string contortions to get at the data for the right form. – Greg Apr 09 '09 at 19:31
  • 2
    I think OP means that it would be nice to manipulate what the form parsed, even if it didn't validate: form.phone_number would return whatever value the field `phone_number` got, regardless of whether it validated correctly. – Carl G Jan 28 '10 at 05:20
  • 1
    Invalid data? I don't get it. Either the data's valid and can be processed or not valid. If you want to relax the rules to allow more (and perhaps do other cleanup) that's what field-level validation is for. Once the field-level validation is done, it's either valid or the whole form is useless. – S.Lott Jan 28 '10 at 11:45
14

I was struggling with a similar issue, and came across a great discussion here: https://code.djangoproject.com/ticket/10427

It's not at all well documented, but for a live form, you can view a field's value -- as seen by widgets/users -- with the following:

form_name['field_name'].value()
Botz3000
  • 39,020
  • 8
  • 103
  • 127
Sam
  • 141
  • 1
  • 2
  • This worked for me with a `MultipleHiddenInput` better than @Dmitry's answer, because inside the template `form.data.field_name.` returns just one item while `form.field_name.value` returns the whole list. – augustomen Mar 08 '13 at 18:52
  • Is there a way to update this field? If `is_valid()` is false? – KVISH Jun 13 '14 at 18:54
  • This is fine, but doesn't work well for formsets because the field_name is something like 'form_0_field_name' so not ideal until you have cleaned_data. In this case you have to clean the form first. Is there a better way? – radtek Aug 27 '14 at 13:31
11

I have many methods. All you can pick.

I suppose the form is like as below:

class SignupForm(forms.Form):
    email = forms.CharField(label='email')
    password = forms.CharField(label='password',
                               widget=forms.PasswordInput)

1-1. Get from request

def signup(req):
    if req.method == 'POST':
        email = req.POST.get('email', '')
        password = req.POST.get('password', '')

2-1. Get the raw value assigned to the field and return the value of the data attribute of field

def signup(req):
    if req.method == 'POST':
        ...
        sf = SignupForm(req.POST)
        email = sf["email"].data
        password = sf["password"].data
        ...

2-2. Get the raw value assigned to the field and return the value of the value attribute of field

def signup(req):
    if req.method == 'POST':
        ...
        sf = SignupForm(req.POST)
        email = sf["email"].value()
        password = sf["password"].value()
        ...

2-3. Get the dictionary assigned to the fields

def signup(req):
    if req.method == 'POST':
        ...
        sf = SignupForm(req.POST)
        # print sf.data
        # <QueryDict: {u'csrfmiddlewaretoken': [u'U0M9skekfcZiyk0DhlLVV1HssoLD6SGv'], u'password': [u''], u'email': [u'hello']}>
        email = sf.data.get("email", '')
        password = sf.data.get("password", '')
        ...
Burger King
  • 2,945
  • 3
  • 20
  • 45
5

You can use this pattern:

class MyForm(forms.Form):
    ...
    def clean(self):
        self.saved_data=self.cleaned_data
        return self.cleaned_data

In your code:

if form.is_valid():
    form.save()
    return django.http.HttpResponseRedirect(...)
if form.is_bound:
    form.saved_data['....'] # cleaned_data does not exist any more, but saved_data does.

Using form.data is not a good solution. Reasons:

  • If the form has a prefix, the dictionary keys will be prefixed with this prefix.
  • The data in form.data is not cleaned: There are only string values.
guettli
  • 25,042
  • 81
  • 346
  • 663
4

I ran into a similar problem using a formset. In my example, I wanted the user to select a 1st choice before a 2nd choice, but if the 1st choice hit another error, the 'select 1st choice before 2nd' error was also displayed.

To grab the 1st field's uncleaned data, I used this within the form field's clean method:

dirty_rc1 = self.data[self.prefix + '-reg_choice_1']

Then, I could test for the presence of data in that field:

if not dirty_rc1:
    raise ValidationError('Make a first choice before second')

Hope this helps!

Dashdrum
  • 657
  • 1
  • 4
  • 9
1

You access the data from either the field's clean() method, or from the form's clean() method. clean() is the function that determines whether the form is valid or not. It's called when is_valid() is called. In form's clean() you have the cleaned_data list when you can run through custom code to make sure it's all checked out. In the widget, you have a clean() also, but it uses a single passed variable. In order to access the field's clean() method, you'll have to subclass it. e.g.:

class BlankIntField(forms.IntegerField):
    def clean(self, value):
        if not value:
            value = 0
        return int(value)

If you want an IntField that doesn't choke on an empty value, for instance, you'd use the above.

clean() on a form kind of works like this:

def clean(self):
    if self.cleaned_data.get('total',-1) <= 0.0:
        raise forms.ValidationError("'Total must be positive")
    return self.cleaned_data

Also you can have a clean_FIELD() function for each field so you can validate each field individually (after the field's clean() is called)

priestc
  • 33,060
  • 24
  • 83
  • 117