Opened 14 years ago

Last modified 5 months ago

#1873 new New feature

Multi-select admin filter for RelatedFields

Reported by: marc@… Owned by: nobody
Component: contrib.admin Version:
Severity: Normal Keywords: nfa-changelist
Cc: msaelices@…, Luis Bruno, Andy Baker, cmawebsite@… Triage Stage: Accepted
Has patch: yes Needs documentation: yes
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: yes


Currently, you can only select one value from a RelatedField filter in de admin interface.
This patch provides a multi-select filter, and an AND/OR switch for the values.

The patch is at

Screenshots are at

This replaces the current single-select filter, but could be used side-by-side if there was some mechanism to choose which filter to use for a particular field in the admin interface.

Todo: test what happens with multiple RelatedField filters.

Attachments (1)

multi-select-relatedfilterspec.2.diff (17.6 KB) - added by marc@… 14 years ago.

Download all attachments as: .zip

Change History (29)

Changed 14 years ago by marc@…

comment:1 Changed 14 years ago by Malcolm Tredinnick <malcolm@…>

A couple of thoughts from reading through this patch (I have not tested it very thorougly yet):

  1. Some extra changes appear to have snuck in. In particular, the change in contrib/auth/ does not look related. Can you check that you are only including changes relevant to this ticket in the diff?
  2. In db/models/fields/, your __repr__() method shoudl really be a __str__() (since it is returning a human-readable version). The problem is that it now does not tell you what the class is, so in general it is less useful. You could create a __str__ method and use that in the place where you want it (in
  3. Probably should remove the commented code in in the final version. I realise you are explaining your logic at the moment, which is good (it certainly helped me). And then the 'else' clause with the nested 'if' and a 'pass' can just become 'elif', which looks a bit easier to read.
  4. Can you explain the comment in about COUNT(*) in db/models/ a bit more? It doesn't seem appropriate for final code, but it would be good to know what needs to be avoided. Is it more complicated than needing to do COUNT(DISTINCT(id_column)) there?
  5. In db/models/, it looks like you are adding a general option for grouping to the extra() args that can be passed to a QuerySet? Is this correct? If so, it might be worth separating that out and getting it right first (multiple grouping clauses and merging extra args with existing grouping are two areas it looks a little fragile at the moment) with tests, rather than as part of this patch.
  6. In a couple of places you have some_string.split(LISTVALUE_SEPARATOR). Isn't this going to fail badly if some of the values are strings contain commas? Or can things only be numeric ids at this point (which does not seem right, since non-numeric primary keys are permitted in Django)? Normally you need to split on commas and then work out how they would be escaped and rejoin if the tail of one fragment is the escape character(s). The escaping character can vary slightly between databases, too, from memory. Need to check how Django is doing this generically so that we can do it in a portable fashion.

Anyway, nice work. The code looks like the right idea to me. Not sure if I'll use it myself, but some people might find it useful. I have no idea if the maintainers want to accept this idea or not (might be a good idea to get an indication on django-developers before going too much further in case it's a flop).

I'd be interested in knowing if you are trying to add a generic 'grouping' section to QuerySet.extra(), because we should then find out if Adrian et al want to add it and get it completely general, if so.

comment:2 Changed 14 years ago by marc@…

Point 1 is also valid for 2 and 3, these changes are unrelated to the actual ticket, it is just that these snuck in while making the screenshots. I will remove the changes for contrib/auth/, core/ and db/models/fields/ entirely.

Points 4 through 6 are spot-on and I'll look into that when I have a bit of time (which should be within a day or two). This patch+ticket was mainly intended to see if this idea is acceptable, and if so, if it is going in the right direction.

The thread is on django-dev as

comment:3 Changed 14 years ago by marc@…

Getting back to point 4, why not a COUNT(*) or COUNT(DISTINCT(*)).

The usual sql statement (simplified, leaving out the joins) looks like

  select count(*)
  from auth_user_groups
  where group_id in (2,3)

This returns one row with the correct count. Note that the in(2,3) means that there are duplicates in the resultset, so a count(distinct(*)) and later a select distinct are necessary.

Now, to implement the and/or functionality, I have modified the sql to look like

  select count(*)
  from auth_user_groups
  where group_id in (2,3)
  group by user_id
  having count(user_id)>= X

with X=1 for OR, and X=len(id_list) for AND.

By using a GROUP BY, I no longer get a single result, but I get a row for each user that satisfies the criteria. The count(*) for each row is now the number of matches for the group_id in (id_list).
For example, the AND case yields

2 2
4 2

And the OR case

3 1
2 2
4 2

Basically, the number of rows returned is once again the actual number of rows.
So, to really tackle this without fetching the rows, I could use (but I don't do that yet) a subselect, like this

  select count(*) from (
    select count(*)
    from auth_user_groups
    where group_id in (2,3)
    group by user_id
    having count(user_id)>= X
  ) as temp

That should work in most database engines (it does in Postgres), but I know older MySQL versions didn't support subselects.

comment:4 Changed 14 years ago by marc@…

I forgot to mention that the GROUP BY gets rid of the duplicates quite nicely :)

comment:5 Changed 14 years ago by Malcolm Tredinnick

Came across this again whilst going through all the patches we have outstanding. One comment on the earlier count(*) stuff: if we ever need count(distinct(...)), not that count(distinct(*)) is not supported in SQLite. You have to do count(distint(col_name)) there. Since the primary key is unique and not NULL, it acts as a good substitute for '*'.

Also are selects in the "from" clause supported everywhere (MySQL and SQLite being ther two most likely infringers here). Haven't checked this yet, just noting it as something to resolve. Also, would we be screwing over the MS SQL Server or Firebird guys at all (Oracle does support selects in the from clause, as does PostgreSQL, so no problems with those two).

comment:6 Changed 14 years ago by hi-world cup

Cc: hi-world cup added
Keywords: rthml tab space editor js added
Summary: [patch] multi-select admin filter for RelatedFieldshi-world cup

comment:7 Changed 14 years ago by Adrian Holovaty

Summary: hi-world cup[patch] multi-select admin filter for RelatedFields

comment:8 Changed 13 years ago by Gary Wilson <gary.wilson@…>

Cc: hi-world cup removed
Keywords: rthml tab space editor js removed
Needs documentation: set
Patch needs improvement: set
Triage Stage: UnreviewedDesign decision needed

no more screenshots :(

comment:9 Changed 13 years ago by marc@…

Oops, sorry, they're back now! (hadn't realized this was still under consideration while cleaning up some stuff)

comment:10 Changed 13 years ago by Jacob

Triage Stage: Design decision neededSomeday/Maybe

At some point, we'll be reworking the admin interface, and this will be one of the things we examine at that time.

comment:11 Changed 13 years ago by sanrio

I am very interested in this feature. The patch (i.e. the diff file) works off a really old
version of django. Is it possible to update the patch to work with either django 0.96
or trunk code base? I tried to manually update the files, but has changed significantly.

comment:12 Changed 12 years ago by jakub_vysoky

Keywords: nfa-changelist added

comment:13 in reply to:  5 Changed 12 years ago by dan.p.burke@…

Replying to mtredinnick:
SQL Server can happily do SELECT in FROM

comment:14 Changed 12 years ago by Manuel Saelices

Cc: msaelices@… added

comment:15 Changed 10 years ago by Luis Bruno

Cc: Luis Bruno added

I'm very interested in this patch's functionality.

comment:16 Changed 9 years ago by Luis Bruno

Owner: changed from nobody to Russell Keith-Magee

Sorry for putting this on your plate, but I think this ticket has slipped through the gaps; mind keeping an eye on it, or putting the ticket on the right guy's plate?

comment:17 Changed 9 years ago by Russell Keith-Magee

Owner: changed from Russell Keith-Magee to nobody

Don't assign tickets to other people. If you want someone to take a look at it, raise the issue on Django-dev, and see who takes the bait.

comment:18 Changed 9 years ago by Łukasz Rekucki

Severity: normalNormal
Summary: [patch] multi-select admin filter for RelatedFieldsMulti-select admin filter for RelatedFields
Type: enhancementNew feature

comment:19 Changed 9 years ago by Julien Phalip

UI/UX: set

comment:20 Changed 8 years ago by Aymeric Augustin

UI/UX: unset

Change UI/UX from NULL to False.

comment:21 Changed 8 years ago by Aymeric Augustin

Easy pickings: unset

Change Easy pickings from NULL to False.

comment:22 Changed 8 years ago by Aymeric Augustin

UI/UX: set

Revert accidental batch modification.

comment:23 Changed 8 years ago by Andy Baker

Cc: Andy Baker added

comment:24 Changed 8 years ago by Julien Phalip

Triage Stage: Someday/MaybeAccepted

comment:25 Changed 6 years ago by Collin Anderson

Cc: cmawebsite@… added

comment:26 Changed 4 years ago by Collin Anderson

I finally thought of a possible way to do this:

Use urls like ?fieldname__in=4&fieldname__in=3

We'd need to support that both in the front end (generating those urls), and in the backend (handling repeated parameters and making sure they become lists).

I recommend only supporting the OR case (or at least starting with that). It's the expected user experience on the web, and I think it would be the most useful. (AND would only be useful in some cases for many to many's.)

comment:27 Changed 3 years ago by Collin Anderson

Today I learned: Django admin already handles ?somefield__in=6,8,10 as you would hope, there's just no UI for it.

comment:28 Changed 5 months ago by Carlton Gibson

Duplicates, looking for filters submitting multiple values in #31338 and #29121.

For a query string like ?key=val1&key=val2&key=val3, `ChangeList.__init__()` drops all but the last value:

        self.params = dict(request.GET.items())

Even if we didn't ship filters using this, accepting multiple params would let users implement such filters themselves.

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