diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
index ff66d3e..c4f7ca7 100644
--- a/django/contrib/admin/filters.py
+++ b/django/contrib/admin/filters.py
@@ -54,6 +54,9 @@ class ListFilter(object):
         """
         raise NotImplementedError
 
+    def limit_lookup_choices(self, queryset):
+        pass
+
 
 class SimpleListFilter(ListFilter):
     # The parameter that should be used in the query string for that filter.
@@ -118,6 +121,7 @@ class FieldListFilter(ListFilter):
         self.field = field
         self.field_path = field_path
         self.title = getattr(field, 'verbose_name', field_path)
+        self.limited_qs = None
         super(FieldListFilter, self).__init__(
             request, params, model, model_admin)
         for p in self.expected_parameters():
@@ -134,6 +138,9 @@ class FieldListFilter(ListFilter):
         except ValidationError as e:
             raise IncorrectLookupParameters(e)
 
+    def limit_lookup_choices(self, queryset):
+        self.limited_qs = queryset
+
     @classmethod
     def register(cls, test, list_filter_class, take_priority=False):
         if take_priority:
@@ -157,23 +164,23 @@ class FieldListFilter(ListFilter):
 
 class RelatedFieldListFilter(FieldListFilter):
     def __init__(self, field, request, params, model, model_admin, field_path):
-        other_model = get_model_from_relation(field)
+        self.other_model = get_model_from_relation(field)
         if hasattr(field, 'rel'):
             rel_name = field.rel.get_related_field().name
         else:
-            rel_name = other_model._meta.pk.name
+            rel_name = self.other_model._meta.pk.name
         self.lookup_kwarg = '%s__%s__exact' % (field_path, rel_name)
         self.lookup_kwarg_isnull = '%s__isnull' % field_path
         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
         self.lookup_val_isnull = request.GET.get(
                                       self.lookup_kwarg_isnull, None)
-        self.lookup_choices = field.get_choices(include_blank=False)
+        self._lookup_choices = None
         super(RelatedFieldListFilter, self).__init__(
             field, request, params, model, model_admin, field_path)
         if hasattr(field, 'verbose_name'):
             self.lookup_title = field.verbose_name
         else:
-            self.lookup_title = other_model._meta.verbose_name
+            self.lookup_title = self.other_model._meta.verbose_name
         self.title = self.lookup_title
 
     def has_output(self):
@@ -183,11 +190,24 @@ class RelatedFieldListFilter(FieldListFilter):
             extra = 1
         else:
             extra = 0
-        return len(self.lookup_choices) + extra > 1
+        return len(self.lookup_choices) + extra > 0
 
     def expected_parameters(self):
         return [self.lookup_kwarg, self.lookup_kwarg_isnull]
 
+    def _get_lookup_choices(self):
+        if self._lookup_choices is None:
+            if self.limited_qs is None:
+                self._lookup_choices = self.field.get_choices(include_blank=False)
+            else:
+                pks = (self.limited_qs
+                       .distinct()
+                       .values_list(self.field_path, flat=True))
+                self._lookup_choices = [(x._get_pk_val(), smart_text(x)) for x in
+                                        self.other_model.objects.filter(pk__in=pks)]
+        return self._lookup_choices
+    lookup_choices = property(_get_lookup_choices)
+
     def choices(self, cl):
         from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
         yield {
@@ -233,10 +253,22 @@ class BooleanFieldListFilter(FieldListFilter):
         return [self.lookup_kwarg, self.lookup_kwarg2]
 
     def choices(self, cl):
-        for lookup, title in (
-                (None, _('All')),
-                ('1', _('Yes')),
-                ('0', _('No'))):
+        lookup, title = None, _('All')
+        yield {
+            'selected': self.lookup_val == lookup and not self.lookup_val2,
+            'query_string': cl.get_query_string({
+                    self.lookup_kwarg: lookup,
+                }, [self.lookup_kwarg2]),
+            'display': title,
+        }
+        choices = self.lookup_choices()
+        for choice in choices:
+            if choice is None:
+                continue
+            elif choice:
+                lookup, title = '1', _('Yes')
+            else:
+                lookup, title = '0', _('No')
             yield {
                 'selected': self.lookup_val == lookup and not self.lookup_val2,
                 'query_string': cl.get_query_string({
@@ -244,7 +276,7 @@ class BooleanFieldListFilter(FieldListFilter):
                     }, [self.lookup_kwarg2]),
                 'display': title,
             }
-        if isinstance(self.field, models.NullBooleanField):
+        if None in choices:
             yield {
                 'selected': self.lookup_val2 == 'True',
                 'query_string': cl.get_query_string({
@@ -253,6 +285,19 @@ class BooleanFieldListFilter(FieldListFilter):
                 'display': _('Unknown'),
             }
 
+    def lookup_choices(self):
+        if self.limited_qs is None:
+            if isinstance(self.field, models.NullBooleanField):
+                choices = (True, False, None)
+            else:
+                choices = (True, False)
+        else:
+            choices = (self.limited_qs
+                       .distinct()
+                       .values_list(self.field_path, flat=True))
+            choices = tuple(ch for ch in (True, False, None) if ch in choices)
+        return choices
+
 FieldListFilter.register(lambda f: isinstance(f,
     (models.BooleanField, models.NullBooleanField)), BooleanFieldListFilter)
 
@@ -261,9 +306,13 @@ class ChoicesFieldListFilter(FieldListFilter):
     def __init__(self, field, request, params, model, model_admin, field_path):
         self.lookup_kwarg = '%s__exact' % field_path
         self.lookup_val = request.GET.get(self.lookup_kwarg)
+        self._lookup_choices = None
         super(ChoicesFieldListFilter, self).__init__(
             field, request, params, model, model_admin, field_path)
 
+    def has_output(self):
+        return len(self.lookup_choices) > 0
+
     def expected_parameters(self):
         return [self.lookup_kwarg]
 
@@ -273,7 +322,7 @@ class ChoicesFieldListFilter(FieldListFilter):
             'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
             'display': _('All')
         }
-        for lookup, title in self.field.flatchoices:
+        for lookup, title in self.lookup_choices:
             yield {
                 'selected': smart_text(lookup) == self.lookup_val,
                 'query_string': cl.get_query_string({
@@ -281,6 +330,25 @@ class ChoicesFieldListFilter(FieldListFilter):
                 'display': title,
             }
 
+    def _get_lookup_choices(self):
+        if self._lookup_choices is None:
+            if self.limited_qs is None:
+                self._lookup_choices = self.field.flatchoices
+            else:
+                choices_dict = dict(self.field.flatchoices)
+                choices = (self.limited_qs
+                           .distinct()
+                           .order_by(self.field_path)
+                           .values_list(self.field_path, flat=True))
+                self._lookup_choices = []
+                for choice in choices:
+                    if choice is not None:
+                        title = force_text(choices_dict.get(choice, choice),
+                                           strings_only=True)
+                        self._lookup_choices.append((choice, title))
+        return self._lookup_choices
+    lookup_choices = property(_get_lookup_choices)
+
 FieldListFilter.register(lambda f: bool(f.choices), ChoicesFieldListFilter)
 
 
@@ -302,30 +370,18 @@ class DateFieldListFilter(FieldListFilter):
             today = now.date()
         tomorrow = today + datetime.timedelta(days=1)
 
+        self.today = today
+        self.tomorrow = tomorrow
+
         self.lookup_kwarg_since = '%s__gte' % field_path
         self.lookup_kwarg_until = '%s__lt' % field_path
-        self.links = (
-            (_('Any date'), {}),
-            (_('Today'), {
-                self.lookup_kwarg_since: str(today),
-                self.lookup_kwarg_until: str(tomorrow),
-            }),
-            (_('Past 7 days'), {
-                self.lookup_kwarg_since: str(today - datetime.timedelta(days=7)),
-                self.lookup_kwarg_until: str(tomorrow),
-            }),
-            (_('This month'), {
-                self.lookup_kwarg_since: str(today.replace(day=1)),
-                self.lookup_kwarg_until: str(tomorrow),
-            }),
-            (_('This year'), {
-                self.lookup_kwarg_since: str(today.replace(month=1, day=1)),
-                self.lookup_kwarg_until: str(tomorrow),
-            }),
-        )
+        self._links = None
         super(DateFieldListFilter, self).__init__(
             field, request, params, model, model_admin, field_path)
 
+    def has_output(self):
+        return len(self.links) > 1
+
     def expected_parameters(self):
         return [self.lookup_kwarg_since, self.lookup_kwarg_until]
 
@@ -338,6 +394,36 @@ class DateFieldListFilter(FieldListFilter):
                 'display': title,
             }
 
+    def _get_links(self):
+        if self._links is None:
+            self._links = []
+            qs = self.limited_qs
+            queries = (
+                (_('Today'), (
+                    (self.lookup_kwarg_since, self.today),
+                    (self.lookup_kwarg_until, self.tomorrow),
+                )),
+                (_('Past 7 days'), (
+                    (self.lookup_kwarg_since, self.today - datetime.timedelta(days=7)),
+                    (self.lookup_kwarg_until, self.tomorrow),
+                )),
+                (_('This month'), (
+                    (self.lookup_kwarg_since, self.today.replace(day=1)),
+                    (self.lookup_kwarg_until, self.tomorrow),
+                )),
+                (_('This year'), (
+                    (self.lookup_kwarg_since, self.today.replace(day=1, month=1)),
+                    (self.lookup_kwarg_until, self.tomorrow),
+                )),
+            )
+            self._links.append((_('Any date'), {}))
+            for title, query in queries:
+                if qs is None or qs.filter(**dict(query)).exists():
+                    self._links.append((title, dict((k, str(v)) for k, v in query)))
+        return self._links
+    links = property(_get_links)
+
+
 FieldListFilter.register(
     lambda f: isinstance(f, models.DateField), DateFieldListFilter)
 
@@ -352,21 +438,22 @@ class AllValuesFieldListFilter(FieldListFilter):
         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
         self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull,
                                                  None)
-        parent_model, reverse_path = reverse_field_path(model, field_path)
+        parent_model, self.reverse_path = reverse_field_path(model, field_path)
         queryset = parent_model._default_manager.all()
         # optional feature: limit choices base on existing relationships
         # queryset = queryset.complex_filter(
         #    {'%s__isnull' % reverse_path: False})
         limit_choices_to = get_limit_choices_to_from_path(model, field_path)
         queryset = queryset.filter(limit_choices_to)
+        self.field_qs = queryset
 
-        self.lookup_choices = (queryset
-                               .distinct()
-                               .order_by(field.name)
-                               .values_list(field.name, flat=True))
+        self._lookup_choices = None
         super(AllValuesFieldListFilter, self).__init__(
             field, request, params, model, model_admin, field_path)
 
+    def has_output(self):
+        return len(self.lookup_choices) > 0
+
     def expected_parameters(self):
         return [self.lookup_kwarg, self.lookup_kwarg_isnull]
 
@@ -401,4 +488,19 @@ class AllValuesFieldListFilter(FieldListFilter):
                 'display': EMPTY_CHANGELIST_VALUE,
             }
 
+    def _get_lookup_choices(self):
+        if self._lookup_choices is None:
+            if self.limited_qs is None:
+                qs = self.field_qs
+                field_name = self.field.name
+            else:
+                qs = self.limited_qs
+                field_name = self.field_path
+            self._lookup_choices = (qs
+                                    .distinct()
+                                    .order_by(field_name)
+                                    .values_list(field_name, flat=True))
+        return self._lookup_choices
+    lookup_choices = property(_get_lookup_choices)
+
 FieldListFilter.register(lambda f: True, AllValuesFieldListFilter)
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index b35f100..ee8759f 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -345,6 +345,7 @@ class ModelAdmin(BaseModelAdmin):
     list_display = ('__str__',)
     list_display_links = ()
     list_filter = ()
+    list_filter_incremental = False
     list_select_related = False
     list_per_page = 100
     list_max_show_all = 200
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py
index a02bb7a..1efbcd7 100644
--- a/django/contrib/admin/validation.py
+++ b/django/contrib/admin/validation.py
@@ -163,10 +163,11 @@ def validate(cls, model):
     if hasattr(cls, "readonly_fields"):
         check_readonly_fields(cls, model, opts)
 
+    # list_filter_incremental = False
     # list_select_related = False
     # save_as = False
     # save_on_top = False
-    for attr in ('list_select_related', 'save_as', 'save_on_top'):
+    for attr in ('list_filter_incremental', 'list_select_related', 'save_as', 'save_on_top'):
         if not isinstance(getattr(cls, attr), bool):
             raise ImproperlyConfigured("'%s.%s' should be a boolean."
                     % (cls.__name__, attr))
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 050d477..9b78561 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -158,7 +158,8 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)):
                     use_distinct = (use_distinct or
                                     lookup_needs_distinct(self.lookup_opts,
                                                           field_path))
-                if spec and spec.has_output():
+                incremental = self.model_admin.list_filter_incremental
+                if spec and (incremental or spec.has_output()):
                     filter_specs.append(spec)
 
         # At this point, all the parameters used by the various ListFilters
@@ -330,16 +331,24 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)):
 
     def get_queryset(self, request):
         # First, we collect all the declared list filters.
-        (self.filter_specs, self.has_filters, remaining_lookup_params,
+        (filter_specs, self.has_filters, remaining_lookup_params,
          use_distinct) = self.get_filters(request)
 
         # Then, we let every list filter modify the queryset to its liking.
         qs = self.root_queryset
-        for filter_spec in self.filter_specs:
+        for filter_spec in filter_specs:
             new_qs = filter_spec.queryset(request, qs)
             if new_qs is not None:
                 qs = new_qs
 
+        # incremental list filter, limit lookup choices
+        if self.model_admin.list_filter_incremental:
+            for filter_spec in filter_specs:
+                filter_spec.limit_lookup_choices(qs)
+
+        # only filters with output are saved
+        self.filter_specs = [fs for fs in filter_specs if fs.has_output()]
+
         try:
             # Finally, we apply the remaining lookup parameters from the query
             # string (i.e. those that haven't already been processed by the
