Skip to content

Commit c5fa377

Browse files
authored
Merge pull request #124 from AltSchool/fix/boolean-filter
Convert filter values to booleans for boolean fields
2 parents 9ad7187 + 6926b73 commit c5fa377

File tree

6 files changed

+69
-5
lines changed

6 files changed

+69
-5
lines changed

dynamic_rest/filters.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.utils import six
77
from rest_framework import serializers
88
from rest_framework.exceptions import ValidationError
9+
from rest_framework.fields import BooleanField, NullBooleanField
910
from rest_framework.filters import BaseFilterBackend, OrderingFilter
1011

1112
from dynamic_rest.conf import settings
@@ -81,6 +82,7 @@ def generate_query_key(self, serializer):
8182
rewritten = []
8283
last = len(self.field) - 1
8384
s = serializer
85+
field = None
8486
for i, field_name in enumerate(self.field):
8587
# Note: .fields can be empty for related serializers that aren't
8688
# sideloaded. Fields that are deferred also won't be present.
@@ -128,7 +130,7 @@ def generate_query_key(self, serializer):
128130
if self.operator:
129131
rewritten.append(self.operator)
130132

131-
return '__'.join(rewritten)
133+
return ('__'.join(rewritten), field)
132134

133135

134136
class DynamicFilterBackend(BaseFilterBackend):
@@ -283,7 +285,9 @@ def _filters_to_query(self, includes, excludes, serializer, q=None):
283285
def rewrite_filters(filters, serializer):
284286
out = {}
285287
for k, node in six.iteritems(filters):
286-
filter_key = node.generate_query_key(serializer)
288+
filter_key, field = node.generate_query_key(serializer)
289+
if isinstance(field, (BooleanField, NullBooleanField)):
290+
node.value = node.value.lower() not in self.FALSEY_STRINGS
287291
out[filter_key] = node.value
288292

289293
return out

tests/migrations/0004_user_is_dead.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9 on 2016-08-08 13:28
3+
from __future__ import unicode_literals
4+
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('tests', '0003_auto_20160401_1656'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='user',
17+
name='is_dead',
18+
field=models.NullBooleanField(default=False),
19+
),
20+
]

tests/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class User(models.Model):
1717
'favorite_pet_type',
1818
'favorite_pet_id',
1919
)
20+
is_dead = models.NullBooleanField(default=False)
2021

2122

2223
class Profile(models.Model):

tests/serializers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ class Meta:
149149
'profile',
150150
'date_of_birth',
151151
'favorite_pet_id',
152-
'favorite_pet'
152+
'favorite_pet',
153+
'is_dead',
153154
)
154155
deferred_fields = (
155156
'last_name',
@@ -159,6 +160,7 @@ class Meta:
159160
'thumbnail_url',
160161
'favorite_pet_id',
161162
'favorite_pet',
163+
'is_dead',
162164
)
163165
read_only_fields = ('profile',)
164166

tests/test_api.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ def setUp(self):
2626
self.fixture = create_fixture()
2727
self.maxDiff = None
2828

29+
def _get_json(self, url, expected_status=200):
30+
response = self.client.get(url)
31+
self.assertEquals(expected_status, response.status_code)
32+
return json.loads(response.content.decode('utf-8'))
33+
2934
def test_get(self):
3035
with self.assertNumQueries(1):
3136
# 1 for User, 0 for Location
@@ -487,7 +492,8 @@ def test_post(self):
487492
"thumbnail_url": None,
488493
"number_of_cats": 1,
489494
"profile": None,
490-
"date_of_birth": None
495+
"date_of_birth": None,
496+
"is_dead": False,
491497
}
492498
})
493499

@@ -783,6 +789,37 @@ def test_get_with_request_filters_and_requires(self):
783789
}]
784790
}, json.loads(response.content.decode('utf-8')))
785791

792+
def test_boolean_filters_on_boolean_field(self):
793+
# create one dead user
794+
User.objects.create(name='Dead', last_name='Mort', is_dead=True)
795+
796+
# set up test specs
797+
tests = {
798+
True: ['true', 'True', '1', 'okies'],
799+
False: ['false', 'False', '0', '']
800+
}
801+
802+
# run through test scenarios
803+
for expected_value, test_values in tests.items():
804+
for test_value in test_values:
805+
url = (
806+
'/users/?include[]=is_dead&filter{is_dead}=%s' % test_value
807+
)
808+
data = self._get_json(url)
809+
810+
expected = set([expected_value])
811+
actual = set([o['is_dead'] for o in data['users']])
812+
self.assertEqual(
813+
expected,
814+
actual,
815+
"Boolean filter '%s' failed. Expected=%s Actual=%s" % (
816+
test_value,
817+
expected,
818+
actual,
819+
)
820+
821+
)
822+
786823

787824
@override_settings(
788825
DYNAMIC_REST={

tests/test_viewsets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def test_is_null_casting(self):
123123
def test_nested_filter_rewrite(self):
124124
node = FilterNode(['members', 'id'], 'in', [1])
125125
gs = GroupSerializer(include_fields='*')
126-
filter_key = node.generate_query_key(gs)
126+
filter_key, field = node.generate_query_key(gs)
127127
self.assertEqual(filter_key, 'users__id__in')
128128

129129

0 commit comments

Comments
 (0)