Opened 3 years ago

Last modified 13 months ago

#19721 new Bug

Django admin allows filtering using the field lookups such as "in", but it is impossible to include a value that contains a comma

Reported by: aruseni Owned by: nobody
Component: contrib.admin Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

The admin site allows you to filter the queryset in the changelist in a plenty of different ways. Notably, it allows you to filter the records by multiple values (if the field's value is one of the specified value options, then such record is considered matching).

For example, you can test it with a query string like this:

/admin/auth/user/?username__in=johnny,viola,gordon

Unfortunately, there is a big limitation at the moment: you can't include a value option that contains a comma (or a few).

The function that splits the string is prepare_lookup_value, found in contrib.admin.util.

Attachments (1)

comma_in__in_data.patch (2.8 KB) - added by chlunde 3 years ago.
Patch with test

Download all attachments as: .zip

Change History (7)

comment:1 Changed 3 years ago by aruseni

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Right now, the following workaround (besides monkey patching prepare_lookup_value) works for me:

def multiple_value_options_filter_factory(field_name):
    """
    This is a class factory. It creates classes used
    for filtering by multiple value options.

    The field options are separated by a "|" character.

    If any of the specified options (or "") is the
    value of the field of a record, then such record
    is considered matching.

    The name of the field that should be using for
    filtering is passed as the argument to the function.
    """
    class MultipleValueOptionsFilter(admin.ListFilter):
        # Human-readable title which will be displayed in the
        # right admin sidebar just above the filter options.
        title = field_name

        # Parameter for the filter that will be used in the URL query.
        parameter_name = "_".join([field_name, "in"])

        def __init__(self, request, params, model, model_admin):
            self.used_parameters = {}
            for p in self.expected_parameters():
                if p in params:
                    value = params.pop(p)
                    self.used_parameters[p] = value.split("|")

        def expected_parameters(self):
            return [self.parameter_name]

        def has_output(self):
            return True

        def choices(self, cl):
            yield {
                'selected': False,
                'query_string': cl.get_query_string({}, [self.parameter_name]),
                'display': 'All',
            }

        def queryset(self, request, queryset):
            """
            Returns the filtered queryset based on the value
            provided in the query string and retrievable via
            `self.value()`.
            """
            value_options = self.used_parameters.get(self.parameter_name, None)
            if not value_options:
                return queryset
            filter_dict = {"__".join([field_name, "in"]): value_options}
            return queryset.filter(**filter_dict)

    return MultipleValueOptionsFilter

I put it in list_filter of the ModelAdmin class:

list_filter = (
    multiple_value_options_filter_factory("some_model_field_to_filter"),
    multiple_value_options_filter_factory("some_other_model_field"),
)

And then including value options that contain commas becomes possible:

?some_model_field_to_filter_in=Look at this, it works now|Yeah, definitely

Changed 3 years ago by chlunde

Patch with test

comment:2 Changed 3 years ago by anonymous

  • Has patch set

Not sure if it's a good idea... I've attached a patch which allows you to escape comma and backslash.

It will break any existing code searching for multiple backslashes (most likely not an issue).

comment:3 Changed 3 years ago by carljm

  • Triage Stage changed from Unreviewed to Accepted

Not sure if the backslash-escape is a good idea, or if we just need to provide a way to easily subclass the standard ListFilter and replace just the separator character (and then document that). Either way, it should be easier to filter on values including a comma.

comment:4 Changed 2 years ago by aaugustin

  • Type changed from Uncategorized to Bug

comment:5 Changed 15 months ago by timo

  • Patch needs improvement set

Patch no longer applies cleanly.

comment:6 Changed 13 months ago by SmileyChris

An interesting way to solve this would be to use getlist() when pulling the filter arguments without an explicit filter lookup and if a list is found, use __in rather than __exact.

So to match the example given in the description, you'd be able to do:
/admin/auth/user/?username=johnny&username=viola,with,comma

Note: See TracTickets for help on using tickets.
Back to Top