Use django-filter 0.6a1 and add database query count tests for paginated, filtered lists.

This commit is contained in:
Tom Christie 2013-03-13 12:45:54 +00:00
parent a798a5350a
commit 73ab7dc3f1
3 changed files with 88 additions and 28 deletions

View File

@ -17,8 +17,8 @@ install:
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.0 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.0 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.3 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.3 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi" - "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi" - "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.6a1 --use-mirrors; fi"
- export PYTHONPATH=. - export PYTHONPATH=.
script: script:

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
from decimal import Decimal from decimal import Decimal
import django
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
@ -20,21 +21,6 @@ class RootView(generics.ListCreateAPIView):
paginate_by = 10 paginate_by = 10
if django_filters:
class DecimalFilter(django_filters.FilterSet):
decimal = django_filters.NumberFilter(lookup_type='lt')
class Meta:
model = FilterableItem
fields = ['text', 'decimal', 'date']
class FilterFieldsRootView(generics.ListCreateAPIView):
model = FilterableItem
paginate_by = 10
filter_class = DecimalFilter
filter_backend = filters.DjangoFilterBackend
class DefaultPageSizeKwargView(generics.ListAPIView): class DefaultPageSizeKwargView(generics.ListAPIView):
""" """
View for testing default paginate_by_param usage View for testing default paginate_by_param usage
@ -119,17 +105,44 @@ class IntegrationTestPaginationAndFiltering(TestCase):
{'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date.isoformat()} {'id': obj.id, 'text': obj.text, 'decimal': obj.decimal, 'date': obj.date.isoformat()}
for obj in self.objects.all() for obj in self.objects.all()
] ]
self.view = FilterFieldsRootView.as_view()
@unittest.skipUnless(django_filters, 'django-filters not installed') @unittest.skipUnless(django_filters, 'django-filters not installed')
def test_get_paginated_filtered_root_view(self): def test_get_django_filter_paginated_filtered_root_view(self):
""" """
GET requests to paginated filtered ListCreateAPIView should return GET requests to paginated filtered ListCreateAPIView should return
paginated results. The next and previous links should preserve the paginated results. The next and previous links should preserve the
filtered parameters. filtered parameters.
""" """
class DecimalFilter(django_filters.FilterSet):
decimal = django_filters.NumberFilter(lookup_type='lt')
class Meta:
model = FilterableItem
fields = ['text', 'decimal', 'date']
class FilterFieldsRootView(generics.ListCreateAPIView):
model = FilterableItem
paginate_by = 10
filter_class = DecimalFilter
filter_backend = filters.DjangoFilterBackend
view = FilterFieldsRootView.as_view()
EXPECTED_NUM_QUERIES = 2
if django.VERSION < (1, 4):
# On Django 1.3 we need to use django-filter 0.5.4
#
# The filter objects there don't expose a `.count()` method,
# which means we only make a single query *but* it's a single
# query across *all* of the queryset, instead of a COUNT and then
# a SELECT with a LIMIT.
#
# Although this is fewer queries, it's actually a regression.
EXPECTED_NUM_QUERIES = 1
request = factory.get('/?decimal=15.20') request = factory.get('/?decimal=15.20')
response = self.view(request).render() with self.assertNumQueries(EXPECTED_NUM_QUERIES):
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 15) self.assertEqual(response.data['count'], 15)
self.assertEqual(response.data['results'], self.data[:10]) self.assertEqual(response.data['results'], self.data[:10])
@ -137,7 +150,8 @@ class IntegrationTestPaginationAndFiltering(TestCase):
self.assertEqual(response.data['previous'], None) self.assertEqual(response.data['previous'], None)
request = factory.get(response.data['next']) request = factory.get(response.data['next'])
response = self.view(request).render() with self.assertNumQueries(EXPECTED_NUM_QUERIES):
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 15) self.assertEqual(response.data['count'], 15)
self.assertEqual(response.data['results'], self.data[10:15]) self.assertEqual(response.data['results'], self.data[10:15])
@ -145,7 +159,53 @@ class IntegrationTestPaginationAndFiltering(TestCase):
self.assertNotEqual(response.data['previous'], None) self.assertNotEqual(response.data['previous'], None)
request = factory.get(response.data['previous']) request = factory.get(response.data['previous'])
response = self.view(request).render() with self.assertNumQueries(EXPECTED_NUM_QUERIES):
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 15)
self.assertEqual(response.data['results'], self.data[:10])
self.assertNotEqual(response.data['next'], None)
self.assertEqual(response.data['previous'], None)
def test_get_basic_paginated_filtered_root_view(self):
"""
Same as `test_get_django_filter_paginated_filtered_root_view`,
except using a custom filter backend instead of the django-filter
backend,
"""
class DecimalFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(decimal__lt=Decimal(request.GET['decimal']))
class BasicFilterFieldsRootView(generics.ListCreateAPIView):
model = FilterableItem
paginate_by = 10
filter_backend = DecimalFilterBackend
view = BasicFilterFieldsRootView.as_view()
request = factory.get('/?decimal=15.20')
with self.assertNumQueries(2):
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 15)
self.assertEqual(response.data['results'], self.data[:10])
self.assertNotEqual(response.data['next'], None)
self.assertEqual(response.data['previous'], None)
request = factory.get(response.data['next'])
with self.assertNumQueries(2):
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 15)
self.assertEqual(response.data['results'], self.data[10:15])
self.assertEqual(response.data['next'], None)
self.assertNotEqual(response.data['previous'], None)
request = factory.get(response.data['previous'])
with self.assertNumQueries(2):
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 15) self.assertEqual(response.data['count'], 15)
self.assertEqual(response.data['results'], self.data[:10]) self.assertEqual(response.data['results'], self.data[:10])

12
tox.ini
View File

@ -8,19 +8,19 @@ commands = {envpython} rest_framework/runtests/runtests.py
[testenv:py3.3-django1.5] [testenv:py3.3-django1.5]
basepython = python3.3 basepython = python3.3
deps = django==1.5 deps = django==1.5
-egit+git://github.com/alex/django-filter.git#egg=django_filter django-filter==0.6a1
defusedxml==0.3 defusedxml==0.3
[testenv:py3.2-django1.5] [testenv:py3.2-django1.5]
basepython = python3.2 basepython = python3.2
deps = django==1.5 deps = django==1.5
-egit+git://github.com/alex/django-filter.git#egg=django_filter django-filter==0.6a1
defusedxml==0.3 defusedxml==0.3
[testenv:py2.7-django1.5] [testenv:py2.7-django1.5]
basepython = python2.7 basepython = python2.7
deps = django==1.5 deps = django==1.5
django-filter==0.5.4 django-filter==0.6a1
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.0 django-oauth-plus==2.0
oauth2==1.5.211 oauth2==1.5.211
@ -29,7 +29,7 @@ deps = django==1.5
[testenv:py2.6-django1.5] [testenv:py2.6-django1.5]
basepython = python2.6 basepython = python2.6
deps = django==1.5 deps = django==1.5
django-filter==0.5.4 django-filter==0.6a1
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.0 django-oauth-plus==2.0
oauth2==1.5.211 oauth2==1.5.211
@ -38,7 +38,7 @@ deps = django==1.5
[testenv:py2.7-django1.4] [testenv:py2.7-django1.4]
basepython = python2.7 basepython = python2.7
deps = django==1.4.3 deps = django==1.4.3
django-filter==0.5.4 django-filter==0.6a1
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.0 django-oauth-plus==2.0
oauth2==1.5.211 oauth2==1.5.211
@ -47,7 +47,7 @@ deps = django==1.4.3
[testenv:py2.6-django1.4] [testenv:py2.6-django1.4]
basepython = python2.6 basepython = python2.6
deps = django==1.4.3 deps = django==1.4.3
django-filter==0.5.4 django-filter==0.6a1
defusedxml==0.3 defusedxml==0.3
django-oauth-plus==2.0 django-oauth-plus==2.0
oauth2==1.5.211 oauth2==1.5.211