Search code examples
pythondjangopython-3.6django-filter

Django Filter Choice Field displays correct values but wont filter


I'm using the Django filter app: https://github.com/carltongibson/django-filter

I'm making a web-comic app and what I'm trying to accomplish is a drop-down filter field that displays the distinct values for that field. In this case, the series field.

After reading their docs. And stack overflow questions like this one or this one . I can make the filter form display the correct drop down values. But when I attempt to filter by them, my result queryset returns no values!

There must be something in the docs I'm not understanding. Here's my hopefully relevant code.

models.py

class ComicPanel(models.Model):
    #... Other fields

    series = models.CharField(max_length = 255, null = False, blank = False, default = getMainSeriesName)

    # Other Filter Fields
    chapter = models.FloatField(null = True, blank = True)

    episode = models.IntegerField(null = True, blank = True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['series','chapter', 'episode'])
            ]

    def __str__(self):
        return self.title

    def save(self):  
        super(ComicPanel, self).save()  

filters.py

import django_filters
from .models import ComicPanel

# Grab Distinct values for series
def getUniqueSeries():
    series_dicts = ComicPanel.objects.all().values_list('series').distinct()
    series_list = []
    i = 0
    for item in series_dicts:
            series_list.append((i,item[0]))
            i = i+1
    return series_list

class ComicPanelFilter(django_filters.FilterSet):

    series = django_filters.ChoiceFilter(choices = getUniqueSeries())

    class Meta:
        model = ComicPanel
        fields = ['chapter', 'episode']

views.py

def view_archive(request):
    comic_list = ComicPanel.objects.all()
    comic_filter = ComicPanelFilter(request.GET, queryset=comic_list)
    paginator = Paginator(comic_filter.qs, 8)

    page = request.GET.get('page', 1)
    try:
        comics = paginator.page(page)
    except (PageNotAnInteger, TypeError):
        comics = paginator.page(1)
    except EmptyPage:
        comics = paginator.page(paginator.num_pages)

    return render(request, 'comics/comic_archive.html', context = {'filter': comic_filter, 'comics': comics})   

template.html

...

<form class="form" method="GET">
    {% csrf_token %}
    <table class="my_classes">
      <thead>
        <tr>
          <th scope="col">Filter Comics: </th>
          <th scope="col"></th>
        </tr>
      </thead>
          <tbody>
                {% for field in filter.form %}
                    <tr>
                        <th scope="row"> {{ field.name }}</th>
                        <td>{{ field }}</td>
                    </tr>
                {% endfor %}                          
          </tbody>
      </table>              
        <button type="submit" class="btn btn-outline-dark">Filter</button>       
</form> 

       ...

{% if comics %}
    {% for comic in comics %}                                   
            ...
    {% endfor %}

<!-- if no comics -->
{% else %}
        <p class = "my_class"> Looks like you don't have any comics! Upload some? </p>
{% endif %}

Results:

My filter form displays the correct, distinct series for the drop down items. But When I filter by any value, the queryset returns no comics

I've also tried using the ModelChoiceFilter in my filters class with a similar result (but the values show up in a Tuple format):

series=django_filters.ModelChoiceFilter(queryset=ComicPanel.objects.all().values_list('series').distinct())

Can someone please tell me what I'm doing wrong?


Solution

  • My mistake was pretty silly actually,

    If we look at the Django docs for field.choices:

    The first element in each tuple is the actual value to be set on the model, and the second element is the human-readable name.

    So this method:

    # Grab Distinct values for series
    def getUniqueSeries():
         series_dicts = ComicPanel.objects.all().values_list('series').distinct()
         series_list = []
         i = 0
         for item in series_dicts:
                series_list.append((i,item[0]))
                i = i+1
         return series_list
    

    returns a list that looks like: [(0,'series1'), (1, 'series2'), ...]

    For my model, the first value of the tuple needs to be the series name. So the list should look like: [('series1','series1'), ('series2', 'series2'), ...]

    So the simple fix is to change this line:

    series_list.append((i,item[0]))
    

    to this:

    series_list.append((item[0],item[0]))