Commit d599052a authored by Adrian Holovaty's avatar Adrian Holovaty
Browse files

Added AllValuesFilterSpec to admin changelist filters, which lets you put any...

Added AllValuesFilterSpec to admin changelist filters, which lets you put any arbitrary field in Admin.list_filter. To determine the list of all available choices, Django does a SELECT DISTINCT. Note this is backwards-incompatible for people who have defined and registered their own FilterSpecs, because each FilterSpec now takes a 'model' parameter.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@3136 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 72307a6e
Loading
Loading
Loading
Loading
+41 −18
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ import datetime

class FilterSpec(object):
    filter_specs = []
    def __init__(self, f, request, params):
    def __init__(self, f, request, params, model):
        self.field = f
        self.params = params

@@ -19,10 +19,10 @@ class FilterSpec(object):
        cls.filter_specs.append((test, factory))
    register = classmethod(register)

    def create(cls, f, request, params):
    def create(cls, f, request, params, model):
        for test, factory in cls.filter_specs:
            if test(f):
                return factory(f, request, params)
                return factory(f, request, params, model)
    create = classmethod(create)

    def has_output(self):
@@ -48,8 +48,8 @@ class FilterSpec(object):
        return "".join(t)

class RelatedFilterSpec(FilterSpec):
    def __init__(self, f, request, params):
        super(RelatedFilterSpec, self).__init__(f, request, params)
    def __init__(self, f, request, params, model):
        super(RelatedFilterSpec, self).__init__(f, request, params, model)
        if isinstance(f, models.ManyToManyField):
            self.lookup_title = f.rel.to._meta.verbose_name
        else:
@@ -77,8 +77,8 @@ class RelatedFilterSpec(FilterSpec):
FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)

class ChoicesFilterSpec(FilterSpec):
    def __init__(self, f, request, params):
        super(ChoicesFilterSpec, self).__init__(f, request, params)
    def __init__(self, f, request, params, model):
        super(ChoicesFilterSpec, self).__init__(f, request, params, model)
        self.lookup_kwarg = '%s__exact' % f.name
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)

@@ -94,8 +94,8 @@ class ChoicesFilterSpec(FilterSpec):
FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)

class DateFieldFilterSpec(FilterSpec):
    def __init__(self, f, request, params):
        super(DateFieldFilterSpec, self).__init__(f, request, params)
    def __init__(self, f, request, params, model):
        super(DateFieldFilterSpec, self).__init__(f, request, params, model)

        self.field_generic = '%s__' % self.field.name

@@ -129,8 +129,8 @@ class DateFieldFilterSpec(FilterSpec):
FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)

class BooleanFieldFilterSpec(FilterSpec):
    def __init__(self, f, request, params):
        super(BooleanFieldFilterSpec, self).__init__(f, request, params)
    def __init__(self, f, request, params, model):
        super(BooleanFieldFilterSpec, self).__init__(f, request, params, model)
        self.lookup_kwarg = '%s__exact' % f.name
        self.lookup_kwarg2 = '%s__isnull' % f.name
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
@@ -150,3 +150,26 @@ class BooleanFieldFilterSpec(FilterSpec):
                   'display': _('Unknown')}

FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec)

# This should be registered last, because it's a last resort. For example,
# if a field is eligible to use the BooleanFieldFilterSpec, that'd be much
# more appropriate, and the AllValuesFilterSpec won't get used for it.
class AllValuesFilterSpec(FilterSpec):
    def __init__(self, f, request, params, model):
        super(AllValuesFilterSpec, self).__init__(f, request, params, model)
        self.lookup_val = request.GET.get(f.name, None)
        self.lookup_choices = model._meta.admin.manager.distinct().order_by(f.name).values(f.name)

    def title(self):
        return self.field.verbose_name

    def choices(self, cl):
        yield {'selected': self.lookup_val is None,
               'query_string': cl.get_query_string({}, [self.field.name]),
               'display': _('All')}
        for val in self.lookup_choices:
            val = str(val[self.field.name])
            yield {'selected': self.lookup_val == val,
                   'query_string': cl.get_query_string({self.field.name: val}),
                   'display': val}
FilterSpec.register(lambda f: True, AllValuesFilterSpec)
+1 −1
Original line number Diff line number Diff line
@@ -574,7 +574,7 @@ class ChangeList(object):
            filter_fields = [self.lookup_opts.get_field(field_name) \
                              for field_name in self.lookup_opts.admin.list_filter]
            for f in filter_fields:
                spec = FilterSpec.create(f, request, self.params)
                spec = FilterSpec.create(f, request, self.params, self.model)
                if spec and spec.has_output():
                    filter_specs.append(spec)
        return filter_specs, bool(filter_specs)