Search code examples
python-3.xdjangodjango-filterdjango-filters

Django-Filter - Removing filter options that will return empty


I feel as if I am missing something pretty obvious here. I'm using Django-Filter to filter a ListView and it's working perfectly apart from the fact the options you get in the dropdown menus also include options that will return an empty search query.

I've pre-populated my database with a bunch of data I'm going to need down the line yet isn't currently in use, the "Nations" and "Region" model's specifically.

Models.py:

class Nation(models.Model):
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=100, unique=False)

class Region(models.Model):
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=100, unique=False)
    nation = models.ForeignKey(Nation, on_delete=models.PROTECT)

class Business(models.Model):
    name = models.CharField(max_length=200)
    nation = models.ManyToManyField(Nation, blank=True,)
    region = models.ManyToManyField(Region, blank=True,)

Filters.py:

from business.models import Business, BusinessCategory
from locations.models import Nation, Region
import django_filters


class BusinessFilter(django_filters.FilterSet):
    nation = django_filters.ModelChoiceFilter(
        field_name='nation', lookup_expr='isnull',
        queryset=Nation.objects.all().order_by('name')
    )

    class Meta:
        model = Business
        fields = ['business_category', 'nation', 'region']

View.Py:

class BusinessIndexView(ListView):
    paginate_by = 6
    template_name = "business/index.html"
    context_object_name = 'all_business'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['filter']= BusinessFilter(self.request.GET, queryset=self.get_queryset())
        return context

    def get_queryset(self):
        return Business.objects.filter(live=True).order_by('-id')

Filter.html:

{% load crispy_forms_tags %}
<div class="mb-2">
<form method="get">
{{filter.form|crispy}}
<button type="submit" class="btn btn-outline-success my-2 my-sm-0">Search</button>
<a class="btn btn-outline-warning my-2 my-sm-0" href="{% url 'business:index' %}">Reset</a>

</form>

</div>

Filters.py is where I know it is breaking down.Any help would be much appreciated. I have read through and tried to implement the relative part of the documentation ("Filtering the related queryset for ModelChoiceFilter") but to no avail.

Edit: The desired functionality is a simple dropdown filter so users can search for a business via the country that the business is associated with.

Example: BBC, UK - Telegraph, UK - CNN, USA- XYZ, USA - etc.

Currently I have a lot of nations added to the database that are not associated with a business yet but will be in the future. I would like to remove "empty" nations from the dropdown list until they have something associated with them.

Using the above example, I have the UK, USA and Canada listed in my filter but only the UK and US should appear as nothing is associated with Canada.


Solution

  • Can you try this:

    In you model:

    class Business(models.Model):
        name = models.CharField(max_length=200)
        nation = models.ManyToManyField(Nation, blank=True,related_name='businesses')
        region = models.ManyToManyField(Region, blank=True,)
    

    Filter.py

    class BusinessFilter(django_filters.FilterSet):
        nation = django_filters.ModelChoiceFilter(
            field_name='nation', lookup_expr='isnull',
            queryset=Nation.objects.exclude(businesses__isnull=True).order_by('name')
        )
    
        class Meta:
            model = Business
            fields = ['business_category', 'nation', 'region']