How do I create a Django ListView with FormMixin?

I have a working ListView with template. I want to filter the list based on the contents of a form, something like:

class SampleListView(FormMixin, ListView):
    model = Sample
    paginate_by = 15
    form_class = LookupForm

    def get_queryset(self):
        samples = Sample.objects.all()
        # filter based on validated form contents
        form = self.get_form()
        if form.is_valid():
            samples = samples.filter(some_property__gte=form.cleaned_data['sample_number']) 
        return samples

class LookupForm(forms.Form):
    sample_number = IntegerField(widget=NumberInput)

The template shows the form as:

<form action="" method='get'>
{% csrf_token %}
{{ form.as_p }}
<input type='submit' value='Query samples'>
</form>

This renders the page with my list and form, but the form always seems to be invalid. How can I integrate the validated form with my ListView? Is this the Avoid anything more complex? That is, should I just not be trying to put the FormMixin and ListView together?

FormMixin looks for data in request.POST by default. Since you are using a GET form for filtering, the form is never actually bound to your data, so is_valid() fails (or the form remains empty).

Override get_form_kwargs to inject the GET data when parameters are present:

def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        # Bind the form to GET data if parameters are present
        if self.request.GET:
            kwargs['data'] = self.request.GET
        return kwargs

This fixes your get_queryset logic and ensures the search values persist in the input fields after the page reloads.

To integrate FormMixin with ListView in Django, follow these steps:

  1. Inherit from ListView and FormMixin:

    from django.views.generic import ListView
    from django.views.generic.edit import FormMixin
    
    class SampleListView(FormMixin, ListView):
        model = Sample
        paginate_by = 15
        form_class = LookupForm
    
  2. Override get_form_kwargs to bind form to GET data:

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        if self.request.GET:
            kwargs['data'] = self.request.GET
        return kwargs
    
  3. Modify get_queryset to filter based on form data:

    def get_queryset(self):
        queryset = super().get_queryset()
        form = self.get_form()
        if form.is_valid():
            queryset = queryset.filter(some_property__gte=form.cleaned_data['sample_number'])
        return queryset
    

This setup ensures your form data is correctly bound and used to filter the list view results.

For more details, refer to the Django documentation on using mixins with class-based views.

Вернуться на верх