$(document).ready(function() { function customFilter(heading, filter, text) { var filtergroup = $('#changelist-filter h3:contains("'+ heading +'")').next('ul'); qs = window.location.search; // if we're already filtering by a sibling (that isn't "All") if (filtergroup.children('li:first').siblings().is('.selected')) { // remove sibling querystring from custom filter querystring var filterBase = filter.substr(0, filter.indexOf('_')); var siblingQs = qs.substr(qs.indexOf(filterBase), qs.indexOf('&', qs.indexOf(filterBase))); if (!siblingQs) { siblingQs = qs.slice(qs.indexOf(filterBase), qs.length); } qs = qs.replace(siblingQs, ''); qs = qs + '&' + filter; } else { // no current filters, make new if (!qs || qs == '?') { qs = '?' + filter; // already filtering by custom filter, ignore } else if (qs.match(filter)) { qs = qs; // current filters, append } else { qs = qs + '&' + filter; } } filtergroup.append('
  • '+ text +'
  • '); var currentfilter = filtergroup.children('li:last'); if (window.location.search.match(filter)) { currentfilter.addClass('selected'); currentfilter.siblings().each(function(){ $(this).removeClass('selected'); $(this).children('a').attr('href', function() { return this.href.replace(filter, ''); }); }); } } // Usage examples. Takes three arguments. // 1. the exact text above your list_display section // 2. the custom field lookup (careful with weird characters here) // 3. the text you want the new filter to show up as customFilter('By affiliation', 'school__in=A,B', 'School A or B'); customFilter('By affiliation', 'school__isnull=True', 'No school'); });