Repository: carltongibson/django-filter

Issue:
duplicate call to filter action
Hi,

I'm trying to use an action with a filter (http://django-filter.readthedocs.io/en/latest/ref/filters.html#action).
The function I'm calling requires some non-trivial processing time, and I've found that it's being called twice, which is really affecting performance. I'd like to find a way to prevent the duplicate call.

i'm using:

Python 3.4.2
Django 1.9.7
djangorestframework 3.3.3
django-filter 0.13.0

Here is a simplified version of my code, with a stack trace printed whenever the action function is called:

```
import traceback

import django_filters
from rest_framework.viewsets import ModelViewSet

from .models import Alert
from .serializers import AlertSerializer


def filter_by_content(queryset, value):
    print('FILTER BY CONTENT')
    traceback.print_stack()
    return queryset


class AlertFilter(django_filters.FilterSet):
    content = django_filters.CharFilter(action=filter_by_content)

    class Meta:
        model = Alert
        fields = ['content']


class AlertViewSet(ModelViewSet):
    queryset = Alert.objects.all()
    filter_class = AlertFilter
    serializer_class = AlertSerializer
```

When I issue a GET request to http://localhost:8000/api/alerts/?content=threat, this is the resulting stack trace, starting from the first call of the filter action:

```
FILTER BY CONTENT
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 889, in _bootstrap
    self._bootstrap_inner()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 921, in _bootstrap_inner
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 869, in run
    self._target(*self._args, **self._kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 613, in process_request_thread
    self.finish_request(request, client_address)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 344, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/servers/basehttp.py", line 99, in __init__
    super(WSGIRequestHandler, self).__init__(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 669, in __init__
    self.handle()
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/servers/basehttp.py", line 179, in handle
    handler.run(self.server.get_app())
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/wsgiref/handlers.py", line 137, in run
    self.result = application(self.environ, self.start_response)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__
    return self.application(environ, start_response)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/handlers/wsgi.py", line 177, in __call__
    response = self.get_response(request)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/viewsets.py", line 87, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/views.py", line 463, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/mixins.py", line 40, in list
    queryset = self.filter_queryset(self.get_queryset())
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/generics.py", line 151, in filter_queryset
    queryset = backend().filter_queryset(self.request, queryset, self)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/filters.py", line 115, in filter_queryset
    return filter_class(request.query_params, queryset=queryset).qs
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django_filters/filterset.py", line 333, in qs
    qs = filter_.filter(qs, value)
  File "/Users/lhadjchikh/Documents/socialops/cyphon/django/alerts/views.py", line 12, in filter_by_content
    traceback.print_stack()
FILTER BY CONTENT
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 889, in _bootstrap
    self._bootstrap_inner()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 921, in _bootstrap_inner
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 869, in run
    self._target(*self._args, **self._kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 613, in process_request_thread
    self.finish_request(request, client_address)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 344, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/servers/basehttp.py", line 99, in __init__
    super(WSGIRequestHandler, self).__init__(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/socketserver.py", line 669, in __init__
    self.handle()
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/servers/basehttp.py", line 179, in handle
    handler.run(self.server.get_app())
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/wsgiref/handlers.py", line 137, in run
    self.result = application(self.environ, self.start_response)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__
    return self.application(environ, start_response)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/handlers/wsgi.py", line 177, in __call__
    response = self.get_response(request)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/core/handlers/base.py", line 172, in get_response
    response = response.render()
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/response.py", line 160, in render
    self.content = self.rendered_content
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/response.py", line 71, in rendered_content
    ret = renderer.render(self.data, media_type, context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/renderers.py", line 676, in render
    context = self.get_context(data, accepted_media_type, renderer_context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/renderers.py", line 655, in get_context
    'filter_form': self.get_filter_form(data, view, request),
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/renderers.py", line 597, in get_filter_form
    html = backend().to_html(request, queryset, view)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/filters.py", line 128, in to_html
    return template_render(template, context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/rest_framework/compat.py", line 241, in template_render
    return template.render(context, request=request)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/backends/django.py", line 95, in render
    return self.template.render(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 206, in render
    return self._render(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 197, in _render
    return self.nodelist.render(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 992, in render
    bit = node.render_annotated(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 959, in render_annotated
    return self.render(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 1043, in render
    output = self.filter_expression.resolve(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 709, in resolve
    obj = self.var.resolve(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 850, in resolve
    value = self._resolve_lookup(context)
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django/template/base.py", line 883, in _resolve_lookup
    current = current[bit]
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django_filters/filterset.py", line 299, in __getitem__
    return self.qs[key]
  File "/Users/lhadjchikh/Documents/socialops/virtualenv/lib/python3.4/site-packages/django_filters/filterset.py", line 333, in qs
    qs = filter_.filter(qs, value)
  File "/Users/lhadjchikh/Documents/socialops/cyphon/django/alerts/views.py", line 12, in filter_by_content
    traceback.print_stack()
[29/Jun/2016 11:00:03] "GET /api/alerts/?content=threat HTTP/1.1" 200 77959
```

Commenting out the `__getitem__` method of `BaseFilterSet` in filterset.py eliminates the second call, and the page still loads normally, but I'm not sure what else this would affect. Is there a change I can make to my own code to prevent the second call to the action, or does this require a change elsewhere?

Any help would be appreciated!



Discussion:

Resolving patch:
diff --git a/django_filters/filterset.py b/django_filters/filterset.py
--- a/django_filters/filterset.py
+++ b/django_filters/filterset.py
@@ -3,6 +3,7 @@
 
 import copy
 import re
+import warnings
 from collections import OrderedDict
 
 from django import forms
@@ -26,6 +27,12 @@
 ORDER_BY_FIELD = 'o'
 
 
+def deprecate(msg):
+    warnings.warn(
+        "%s See: https://django-filter.readthedocs.io/en/latest/migration.html" % msg,
+        DeprecationWarning, stacklevel=3)
+
+
 class STRICTNESS(object):
     """
     Values of False & True chosen for backward compatability reasons.
@@ -291,15 +298,22 @@ def __init__(self, data=None, queryset=None, prefix=None, strict=None):
             filter_.parent = self
 
     def __iter__(self):
+        deprecate('QuerySet methods are no longer proxied.')
         for obj in self.qs:
             yield obj
 
     def __len__(self):
+        deprecate('QuerySet methods are no longer proxied.')
         return self.qs.count()
 
     def __getitem__(self, key):
+        deprecate('QuerySet methods are no longer proxied.')
         return self.qs[key]
 
+    def count(self):
+        deprecate('QuerySet methods are no longer proxied.')
+        return self.qs.count()
+
     @property
     def qs(self):
         if not hasattr(self, '_qs'):
@@ -355,9 +369,6 @@ def qs(self):
 
         return self._qs
 
-    def count(self):
-        return self.qs.count()
-
     @property
     def form(self):
         if not hasattr(self, '_form'):


Test patch:
diff --git a/tests/models.py b/tests/models.py
--- a/tests/models.py
+++ b/tests/models.py
@@ -58,18 +58,6 @@ class User(models.Model):
     def __str__(self):
         return self.username
 
-@python_2_unicode_compatible
-class UsersOfManager(models.Model):
-    users = models.ManyToManyField(User,
-                                   limit_choices_to={'is_active': True},
-                                   related_name='users_of_manager')
-    manager = models.ForeignKey(User,
-                                limit_choices_to=lambda: {'status': 1},
-                                related_name='his_users')
-
-    def __str__(self):
-        return self.manager.name + '_group'
-
 
 @python_2_unicode_compatible
 class ManagerGroup(models.Model):
diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py
new file mode 100644
--- /dev/null
+++ b/tests/test_deprecations.py
@@ -0,0 +1,49 @@
+
+import warnings
+from django.test import TestCase
+
+from django_filters import FilterSet
+from .models import User
+
+
+class UserFilter(FilterSet):
+    class Meta:
+        model = User
+
+
+class FilterSetContainerDeprecationTests(TestCase):
+
+    def test__iter__notification(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+
+            [obj for obj in UserFilter()]
+
+            self.assertEqual(len(w), 1)
+            self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
+
+    def test__getitem__notification(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+
+            with self.assertRaises(IndexError):
+                UserFilter()[0]
+
+            self.assertEqual(len(w), 1)
+            self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
+
+    def test__len__notification(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+
+            len(UserFilter())
+
+            self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
+
+    def test__count__notification(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+
+            UserFilter().count()
+
+            self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
diff --git a/tests/test_filtering.py b/tests/test_filtering.py
--- a/tests/test_filtering.py
+++ b/tests/test_filtering.py
@@ -844,9 +844,9 @@ class Meta:
                 fields = ['username']
 
         self.assertEqual(list(F().qs), list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'alex'})),
+        self.assertEqual(list(F({'username': 'alex'}).qs),
                          [User.objects.get(username='alex')])
-        self.assertEqual(list(F({'username': 'jose'})),
+        self.assertEqual(list(F({'username': 'jose'}).qs),
                          list())
 
     def test_filtering_without_strict(self):
@@ -863,9 +863,9 @@ class Meta:
                 fields = ['username']
 
         self.assertEqual(list(F().qs), list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'alex'})),
+        self.assertEqual(list(F({'username': 'alex'}).qs),
                          [User.objects.get(username='alex')])
-        self.assertEqual(list(F({'username': 'jose'})),
+        self.assertEqual(list(F({'username': 'jose'}).qs),
                          list(User.objects.all()))
 
 
@@ -889,9 +889,9 @@ def filter_username(self, queryset, value):
                 )
 
         self.assertEqual(list(F().qs), list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'alex'})),
+        self.assertEqual(list(F({'username': 'alex'}).qs),
                          [User.objects.get(username='alex')])
-        self.assertEqual(list(F({'username': 'jose'})),
+        self.assertEqual(list(F({'username': 'jose'}).qs),
                          list())
 
     def test_filtering_external(self):
@@ -912,9 +912,9 @@ class Meta:
                 fields = ['username']
 
         self.assertEqual(list(F().qs), list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'alex'})),
+        self.assertEqual(list(F({'username': 'alex'}).qs),
                          [User.objects.get(username='alex')])
-        self.assertEqual(list(F({'username': 'jose'})),
+        self.assertEqual(list(F({'username': 'jose'}).qs),
                          list())
 
 
@@ -936,13 +936,13 @@ def filter_username(self, queryset, value):
                 )
 
         self.assertEqual(list(F().qs), list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'mike'})),
+        self.assertEqual(list(F({'username': 'mike'}).qs),
                          [User.objects.get(username='mike'),
                           User.objects.get(username='jake')],)
-        self.assertEqual(list(F({'username': 'jake'})),
+        self.assertEqual(list(F({'username': 'jake'}).qs),
                          [User.objects.get(username='mike'),
                           User.objects.get(username='jake')])
-        self.assertEqual(list(F({'username': 'aaron'})),
+        self.assertEqual(list(F({'username': 'aaron'}).qs),
                          [User.objects.get(username='mike'),
                           User.objects.get(username='jake')])
 
@@ -961,11 +961,11 @@ class Meta:
                 fields = ['username']
 
         self.assertEqual(list(F().qs), list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'mike'})),
+        self.assertEqual(list(F({'username': 'mike'}).qs),
                          list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'jake'})),
+        self.assertEqual(list(F({'username': 'jake'}).qs),
                          list(User.objects.all()))
-        self.assertEqual(list(F({'username': 'aaron'})),
+        self.assertEqual(list(F({'username': 'aaron'}).qs),
                          list(User.objects.all()))
 
 
@@ -1524,27 +1524,27 @@ def test_numeric_filtering(self):
         qs = User.objects.all()
         f = F(queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'status__in': ''}, queryset=qs)
         self.assertEqual(len(f.qs), 0)
-        self.assertEqual(f.count(), 0)
+        self.assertEqual(f.qs.count(), 0)
 
         f = F({'status__in': '0'}, queryset=qs)
         self.assertEqual(len(f.qs), 1)
-        self.assertEqual(f.count(), 1)
+        self.assertEqual(f.qs.count(), 1)
 
         f = F({'status__in': '0,2'}, queryset=qs)
         self.assertEqual(len(f.qs), 3)
-        self.assertEqual(f.count(), 3)
+        self.assertEqual(f.qs.count(), 3)
 
         f = F({'status__in': '0,,1'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
         f = F({'status__in': '2'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
     def test_string_filtering(self):
         F = self.user_filter
@@ -1552,27 +1552,27 @@ def test_string_filtering(self):
         qs = User.objects.all()
         f = F(queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'username__in': ''}, queryset=qs)
         self.assertEqual(len(f.qs), 0)
-        self.assertEqual(f.count(), 0)
+        self.assertEqual(f.qs.count(), 0)
 
         f = F({'username__in': 'alex'}, queryset=qs)
         self.assertEqual(len(f.qs), 1)
-        self.assertEqual(f.count(), 1)
+        self.assertEqual(f.qs.count(), 1)
 
         f = F({'username__in': 'alex,aaron'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
         f = F({'username__in': 'alex,,aaron'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
         f = F({'username__in': 'alex,'}, queryset=qs)
         self.assertEqual(len(f.qs), 1)
-        self.assertEqual(f.count(), 1)
+        self.assertEqual(f.qs.count(), 1)
 
     def test_datetime_filtering(self):
         F = self.article_filter
@@ -1582,27 +1582,27 @@ def test_datetime_filtering(self):
         qs = Article.objects.all()
         f = F(queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'published__in': ''}, queryset=qs)
         self.assertEqual(len(f.qs), 0)
-        self.assertEqual(f.count(), 0)
+        self.assertEqual(f.qs.count(), 0)
 
         f = F({'published__in': '%s' % (after, )}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
         f = F({'published__in': '%s,%s' % (after, before, )}, queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'published__in': '%s,,%s' % (after, before, )}, queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'published__in': '%s,' % (after, )}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
     def test_related_filtering(self):
         F = self.article_filter
@@ -1610,27 +1610,27 @@ def test_related_filtering(self):
         qs = Article.objects.all()
         f = F(queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'author__in': ''}, queryset=qs)
         self.assertEqual(len(f.qs), 0)
-        self.assertEqual(f.count(), 0)
+        self.assertEqual(f.qs.count(), 0)
 
         f = F({'author__in': '1'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
         f = F({'author__in': '1,2'}, queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'author__in': '1,,2'}, queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'author__in': '1,'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
 
 class MiscFilterSetTests(TestCase):
@@ -1706,19 +1706,19 @@ class Meta:
         qs = User.objects.all()
         f = F(queryset=qs)
         self.assertEqual(len(f.qs), 4)
-        self.assertEqual(f.count(), 4)
+        self.assertEqual(f.qs.count(), 4)
 
         f = F({'status': '0'}, queryset=qs)
         self.assertEqual(len(f.qs), 1)
-        self.assertEqual(f.count(), 1)
+        self.assertEqual(f.qs.count(), 1)
 
         f = F({'status': '1'}, queryset=qs)
         self.assertEqual(len(f.qs), 1)
-        self.assertEqual(f.count(), 1)
+        self.assertEqual(f.qs.count(), 1)
 
         f = F({'status': '2'}, queryset=qs)
         self.assertEqual(len(f.qs), 2)
-        self.assertEqual(f.count(), 2)
+        self.assertEqual(f.qs.count(), 2)
 
     def test_invalid_field_lookup(self):
         # We want to ensure that non existent lookups (or just simple misspellings)
diff --git a/tests/test_filterset.py b/tests/test_filterset.py
--- a/tests/test_filterset.py
+++ b/tests/test_filterset.py
@@ -719,7 +719,6 @@ def get_order_by(self, order_choice):
             f.qs, ['carl', 'alex', 'aaron', 'jacob'], lambda o: o.username)
 
 
-
 class FilterSetTogetherTests(TestCase):
 
     def setUp(self):
@@ -759,3 +758,20 @@ class Meta:
         f = F({'username': 'alex', 'status': 1}, queryset=self.qs)
         self.assertEqual(f.qs.count(), 1)
         self.assertQuerysetEqual(f.qs, [self.alex.pk], lambda o: o.pk)
+
+
+@unittest.skip('remove when relevant deprecations have been completed')
+class MiscFilterSetTests(TestCase):
+
+    def test_no__getitem__(self):
+        # The DTL processes variable lookups by the following rules:
+        # https://docs.djangoproject.com/en/1.9/ref/templates/language/#variables
+        # A __getitem__ implementation precedes normal attribute access, and in
+        # the case of #58, will force the queryset to evaluate when it should
+        # not (eg, when rendering a blank form).
+        self.assertFalse(hasattr(FilterSet, '__getitem__'))
+
+    def test_no_qs_proxying(self):
+        # The FilterSet should not proxy .qs methods - just access .qs directly
+        self.assertFalse(hasattr(FilterSet, '__len__'))
+        self.assertFalse(hasattr(FilterSet, '__iter__'))