'How to omit fields in Django form that are not provided by the request after cleaning?

I have several Django forms which are used in HTTP APIs, and these forms are full of optional fields. For example:

class MySearchForm(forms.Form):
    param1 = forms.CharField(required=False)
    param2 = forms.BooleanField(required=False)
    param3 = forms.IntegerField(required=False, min_value=0)

Now I'm using these to validate parameters or forms in requests, by calling form.is_valid(), just like this:

def on_my_search_request(request):
    form = MySearchForm(request.GET)   # in case of a POST request, request.POST is used
    if form.is_valid():
        do_search(form.cleaned_data)   # do something with form.cleaned_data
    else:
        # response with error

The code above is expected to work properly even if no parameter is given in the request (in this case, apply no condition to the search).

So, I'm expecting form.cleaned_data to contain only the valid fields that appears in the request, which is not the case. After the cleaning, all the fields not provided are given an empty value.

Consider the following request:

https://hostname-of-my-server:port/search?param1=test

And what I need in the do_search function:

{'param1': 'test'}

But the actual value is:

{'param1': 'test', 'param2': False, 'param3': None}
# improperly set or unexpected param2 and param3 will cause problems

Is there an elegant way to omit the fields that are not provided?



Solution 1:[1]

I personally figured out an approach, that is, to override the cleaning function in the Form class:

from django import forms
from django.core.exceptions import ValidationError


class CustomForm(forms.Form):
    """ Only keeps fields explicitly provided in cleaned_data. """

    def _clean_fields(self):
        for name, field in self.fields.items():
            if name not in self.data:   # <<< added
                continue                # <<< added
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, forms.FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)

Not very elegent, and not sure if it works in all cases (enough for myself only).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 PleasantFuban