carltongibson / django-filter

A generic system for filtering Django QuerySets based on user selections

Home Page:https://django-filter.readthedocs.io/en/main/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ChoiceFilter not working as expected with | crispy

opened this issue · comments

Hi all

I'm trying to combine django-filter with crispy-tailwind. The expected behavior is that the ChoiceFilter adds the selected attribute so that the filtered value would be pre-selected. The records are filtered correctly.

This works fine with:

<form method="get">
  {{ filter.form.as_p }}
</form>

But doesn't work with:

<form method="get">
  {{ filter.form | crispy }}
</form>

With | crispy the ChoiceLists look beautiful, but the filtered value is not selected:

image

Any thoughts?

Below you'll find a few lines of code. I think I've extracted the relevant pieces. Full code is available in my objector github repository.

models.py

from django.db import models
from django.utils.translation import gettext_lazy as _
from rules.contrib.models import RulesModel
from inventory.models import Object

class Task(RulesModel):
    class Statuses(models.IntegerChoices):
        OVERDUE = 10, _("Overdue")
        DUE = 20, _("Due")
        PENDING = 30, _("Pending")
        INACTIVE = 40, _("Inactive")

    name = models.CharField(_("name"), max_length=200)
    description = models.TextField(_("description"), blank=True)
    object = models.ForeignKey(
        Object,
        verbose_name=_("object"),
        related_name="task_object",
        on_delete=models.CASCADE,
    )

filters.py

from .models import Task
import django_filters


class TaskFilter(django_filters.FilterSet):
    class Meta:
        model = Task
        fields = ["status", "object"]

views.py


from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Task
from .filters import TaskFilter


class TaskListView(LoginRequiredMixin, ListView):
    model = Task
    paginate_by = 10

    def get_queryset(self):
        # all groups for user
        groups = self.request.user.groups.values_list("pk", flat=True)
        groups_as_list = list(groups)
        queryset = Task.objects.filter(object__owner=self.request.user)
        filterset = TaskFilter(self.request.GET, queryset=queryset)
        return filterset.qs

    def get_context_data(self, **kwargs):
        context = super(TaskListView, self).get_context_data(**kwargs)
        filterset = TaskFilter(self.request.GET, queryset=self.queryset)
        context["filter"] = filterset
        return context

Is the selected attribute being set on the choice? (I would expect so but have a dig into the rendered HTML)

I'm calling the following URL: http://localhost:8000/task/?status=30

With {{ filter.form }} the template generates the following output:

<select name="status" id="id_status">
  <option value="" >---------</option>
  <option value="10" >Overdue</option>
  <option value="20" >Due</option>
  <option value="30" selected>Pending</option>
  <option value="40" >Inactive</option>
</select>

With {{ filter.form | crispy }} the template generates the following output, which is missing the selected value:

<select class="bg-white ..." name="status" >
  <option value="" >---------</option>
  <option value="10" >Overdue</option>
  <option value="20" >Due</option>
  <option value="30" >Pending</option>
  <option value="40" >Inactive</option>
</select>

I wanted to debug my template to figure out which values are passed to crispy, but I didn't manage. :-)

The issue might be in these files: select.html and especially select_option.html. I've compared it to the crispy-bootstrap5 repository, but there seems to be nothing similar available.

On the other hand I'm not sure if the issue is with crispy-tailwind, as there are other dropdown fields on other forms which work as expected. But these fields are on a plain form, not a filter. Thus I've raised the issue with django-filter and not crispy-tailwind.

The form is right, since the as_p() rendering is correct. So it's a missing element in the template pack I'd think.

I think I've fixed it.

In select_option.html:

{% load crispy_forms_filters %}
{% load l10n %}

<option value="{{ value|stringformat:'s' }}" {{ field.field.widget.attrs|flatatt }}{% if field.value|stringformat:'s' == value|stringformat:'s' %} selected{% endif %}>{{ label }}</option>

With adding |stringformat:'s' to the value comparison it works for form fields and the django-filter fields. Would this solution be good enough for a pull request?

I think yes. Do open it on crispy-tailwind. I will let @smithdc1 know there's incoming. 😃

Actually there are two other pull requests on the Select feature.. so my pull request could be obsolete:

Especially the first one would make mine and the second one obsolete.

Who will guide us out on the way forward and when are crispy-tailwind releases usually made public? :-)

@gldecurtins On the last I'd likely suggest installing from GitHub as the crispy-tailwind package is still beta, and there may not be a release imminent.

Here's a link for installing from GitHub: https://pip.pypa.io/en/stable/topics/vcs-support/

I'll close this as I don't think it's a Django-filter issue.