0

I'm using the Django-Filter library !important. For tags, I'm using Django-taggit.

I built the following filter.py:

class TaskFilter(django_filters.FilterSet):
    tags = django_filters.ModelMultipleChoiceFilter(widget=forms.CheckboxSelectMultiple, queryset=Task.tags.most_common())

    class Meta:
        model = Task
        fields = ['tags']

However, when I pass this filter to the template, it doesn't render the tags properly. In particular {{ field.value }} is empty. Let's look at the following cases:


CASE 1.

# template.html
{{ filter.form.tags.errors }}
{% for field in filter.form.tags %}
<label for="{{ field.id_for_label }}"></label>
{{ field.value }}
{% endfor %}

# out
<label for="id_tags_0"></label>
<label for="id_tags_1"></label>
<label for="id_tags_2"></label>

CASE 2.

# template.html
{{ filter.form.tags.errors }}
{% for field in filter.form.tags %}
<label for="{{ field.id_for_label }}"></label>
{{ field }}
{% endfor %}

# out
<label for="id_tags_0"></label>
<label for="id_tags_0"><input type="checkbox" name="tags" value="4" id="id_tags_0">Tag 1</label>
<label for="id_tags_1"></label>
<label for="id_tags_1"><input type="checkbox" name="tags" value="1" id="id_tags_1">Tag 2</label>
<label for="id_tags_2"></label>
<label for="id_tags_2"><input type="checkbox" name="tags" value="2" id="id_tags_2">Tag 3</label>

CASE 3.

# template.html
{{ filter.form.tags.errors }}
{% for field in filter.form.tags %}
{{ field }}
{{ field.label_tag }}
{% endfor %}

#out
<label for="id_tags_0"><input type="checkbox" name="tags" value="4" id="id_tags_0">Tag 1</label>
<label for="id_tags_1"><input type="checkbox" name="tags" value="1" id="id_tags_1">Tag 2</label>
<label for="id_tags_2"><input type="checkbox" name="tags" value="2" id="id_tags_2">Tag 3</label>

I'm trying to understand why this happens. Why can't I get the values as stated in the docs

STEPS TO REPRODUCE

pip install django-filter + add 'django_filters' to APPS
pip install django-taggit + add 'taggit' to APPS

# models.py
class Task(models.Model):
    title = models.CharField(max_length=100, blank=False)
    tags = TaggableManager()

# Use the API to create an object.
t = Task.objects.create(title="Title")
t.tags.add("Tag 1","Tag 2")

# views.py
def view(request):
    f = TaskFilter(request.GET, queryset=Task.objects.all())
    return render(request, 'template.html', {'filter': f}
GRS
  • 2,807
  • 4
  • 34
  • 72

1 Answers1

4

When you iterate over filter.form.tags you're not iterating over a set of form fields, but instead over a set of individual choices for the tags field. This is why field.value doesn't work.

This should work instead:

{{ filter.form.tags.errors }}
{% for choice in filter.form.tags %}
    <label for="{{ choice.id_for_label }}"></label>
    {{ choice.tag }}
{% endfor %}

Where tag is an attribute that exists on each choice, which will render the checkbox input for that choice.

This is documented in the documentation for RadioSelect:

To get more granular, you can use each radio button’s tag, choice_label and id_for_label attributes.

Further down, the documentation for CheckBoxSelectMultiple says that the same logic applies for it too.

solarissmoke
  • 30,039
  • 14
  • 71
  • 73
  • Thanks this explains my question. One more thing, is it possible (using shortcuts, in simple way) to check whether at least one tag from this group is checked? – GRS Apr 03 '18 at 09:01
  • P.S. I can't award the +50 rep for 6 more hours – GRS Apr 03 '18 at 09:02
  • I'm not aware of any shortcut for that - think you would have to iterate over all the choices to find out which ones are selected. – solarissmoke Apr 03 '18 at 10:37
  • 1
    Thanks, I made a solution using `request.GET['tags']` in Django views and passed it to template if anyone has this problem. – GRS Apr 03 '18 at 10:41