views:

163

answers:

1

I'm using the very cool django-filter (via: http://github.com/alex/django-filter) and either can't seem to wrap my head around the docs, or maybe just need a little boost.

When I show the filter form on an object list page, for a FK field I get the drop down that includes a "-----" which results in an "any" type filter. But I have some choices set to a field on that model, and I'd like to get the same "any" type option. Here's a relevant example portion from models.py:

TICKET_STATUS_CHOICES = (
    ('new', 'New'),
    ('accepted', 'Accepted'),
    ('assigned', 'Assigned'),
    ('reopened', 'Reopened'),
    ('closed', 'Closed'),
)

class Ticket(models.Model):
    assigned_to = models.ForeignKey(User, null=True, blank=True)
    status = models.CharField(max_length=20,
choices=TICKET_STATUS_CHOICES, default='new')

import django_filters

class TicketFilter(django_filters.FilterSet):
    class Meta:
        model = Ticket
        fields = ['assigned_to', 'status']

When I display the filter form, 'assigned_to' gets an 'any' option, as well as listing the available users. The 'status' field, however, is limited to only the options listed in the actual '_CHOICES'.

How do I add an 'any' option to the fields based on _CHOICES?

A: 

As mentioned in the short but sweet 'usage' docs,

Filters also take any arbitrary keyword arguments which get passed onto the django.forms.Field constructor.

This didn't make a lot of sense until I looked a bit further. In the ./django-filter/docs/ref/ directory, there's filters.txt which describes the Filter Fields and what Model Fields they interact with by default. (I think I've got the language right here, if not, correct me).

So, we can see that ChoiceFilter is used for any field "with choices".

Hitting up the Django documentation, what's important here is Form Fields, and how they interact with Models. (Using Django Forms). So we find ChoiceField (http://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield) which says

Takes one extra required argument: ChoiceField.choices An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this field.

So you can pass it a list, not just the original Tuple choices set.

So this might not be pythonic or DRY, but here's how I changed the model in question:

class TicketFilter(django_filters.FilterSet):
    class Meta:
        model = Ticket
        fields = ['assigned_to', 'priority', 'status']

    def __init__(self, *args, **kwargs):
        super(TicketFilter, self).__init__(*args, **kwargs)
        self.filters['priority'].extra.update(
            {
                'choices': CHOICES_FOR_PRIORITY_FILTER
            })

And above that, where my _CHOICES were defined, I defined this new one (mentioned above) and made sure to append the original choices to the end:

CHOICES_FOR_PRIORITY_FILTER = [
    ('', 'Any'),
]
CHOICES_FOR_PRIORITY_FILTER.extend(list(TICKET_PRIORITY_CHOICES))

I'm using list() here because the original Choices were set up in a tuple, so I want to turn that into a list. Also, if you're getting a NoneType error, be sure you're not attempting to assign the 'return value' of .extend(), because there isn't one. I tripped on this, because I forgot that it was a method of a list, and not a function that returned a new list.

If you know of an easier, more DRY or "pythonic" way to do this, please let me know!

anonymous coward