Skip to content

Commit

Permalink
Add BooleanWidget handling, fixes #25
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan P Kilby committed Jan 13, 2016
1 parent a4f8c02 commit d3fd92b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 13 deletions.
10 changes: 10 additions & 0 deletions rest_framework_filters/fields.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
from django import forms

# TODO: Remove when django-filter 0.12.0 is released
try:
from django_filters import BooleanWidget
except ImportError:
from .widgets import BooleanWidget


class BooleanField(forms.BooleanField):
widget = BooleanWidget


class ArrayDecimalField(forms.DecimalField):
def clean(self, value):
Expand Down
5 changes: 4 additions & 1 deletion rest_framework_filters/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

def _import_class(path):
module_path, class_name = path.rsplit('.', 1)
class_name = str(class_name) # Ensure not unicode on py2.x
class_name = str(class_name) # Ensure not unicode on py2.x
module = __import__(module_path, fromlist=[class_name], level=0)
return getattr(module, class_name)

Expand Down Expand Up @@ -69,6 +69,9 @@ class AllLookupsFilter(Filter):
###################################################
# Fixed-up versions of some of the default filters
###################################################
class BooleanFilter(BooleanFilter):
field_class = fields.BooleanField


class InSetFilterBase(object):
def filter(self, qs, value):
Expand Down
26 changes: 26 additions & 0 deletions rest_framework_filters/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

from __future__ import absolute_import
from __future__ import unicode_literals

from django import forms


# TODO: Remove when django-filter 0.12.0 is released
class BooleanWidget(forms.Widget):
"""Convert true/false values into the internal Python True/False.
This can be used for AJAX queries that pass true/false from JavaScript's
internal types through.
"""
def value_from_datadict(self, data, files, name):
"""
"""
value = super(BooleanWidget, self).value_from_datadict(
data, files, name)

if value is not None:
if value.lower() == 'true':
value = True
elif value.lower() == 'false':
value = False

return value
1 change: 1 addition & 0 deletions tests/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Meta:
class UserFilter(FilterSet):
username = filters.CharFilter(name='username')
email = filters.CharFilter(name='email')
is_active = filters.BooleanFilter(name='is_active')

class Meta:
model = User
Expand Down
60 changes: 48 additions & 12 deletions tests/test_filterset.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@
import datetime

from django.test import TestCase, override_settings
from django.contrib.auth.models import User
from django.utils.dateparse import parse_time, parse_datetime

from rest_framework_filters import filters

from .models import (
Note, Post, Cover, Page, A, B, C, Person, Tag, BlogPost,
User, Note, Post, Cover, Page, A, B, C, Person, Tag, BlogPost,
)

from .filters import (
NoteFilterWithAll,
# UserFilter,
UserFilter,
# UserFilterWithAll,
NoteFilterWithRelated,
NoteFilterWithRelatedAll,
Expand Down Expand Up @@ -320,15 +319,6 @@ def test_get_filterset_subset(self):

class MethodFilterTests(TestCase):

if django.VERSION >= (1, 8):
@classmethod
def setUpTestData(cls):
cls.generateTestData()

else:
def setUp(self):
self.generateTestData()

@classmethod
def generateTestData(cls):
user = User.objects.create(username="user1", email="user1@example.org")
Expand Down Expand Up @@ -465,6 +455,9 @@ def setUpTestData(cls):
john = Person.objects.create(name="John")
Person.objects.create(name="Mark", best_friend=john)

User.objects.create(username="user1", email="user1@example.org", is_active=True)
User.objects.create(username="user2", email="user2@example.org", is_active=False)

def test_inset_number_filter(self):
p1 = Person.objects.get(name="John").pk
p2 = Person.objects.get(name="Mark").pk
Expand Down Expand Up @@ -563,6 +556,49 @@ def test_dict_declaration(self):
filters.BooleanFilter
)

def test_boolean_filter(self):
# Capitalized True
GET = {'is_active': 'True'}
filterset = UserFilter(GET, queryset=User.objects.all())
results = list(filterset)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].username, 'user1')

# Lowercase True
GET = {'is_active': 'true'}
filterset = UserFilter(GET, queryset=User.objects.all())
results = list(filterset)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].username, 'user1')

# Uppercase True
GET = {'is_active': 'TRUE'}
filterset = UserFilter(GET, queryset=User.objects.all())
results = list(filterset)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].username, 'user1')

# Capitalized False
GET = {'is_active': 'False'}
filterset = UserFilter(GET, queryset=User.objects.all())
results = list(filterset)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].username, 'user2')

# Lowercase False
GET = {'is_active': 'false'}
filterset = UserFilter(GET, queryset=User.objects.all())
results = list(filterset)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].username, 'user2')

# Uppercase False
GET = {'is_active': 'FALSE'}
filterset = UserFilter(GET, queryset=User.objects.all())
results = list(filterset)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].username, 'user2')


class FilterExclusionTests(TestCase):

Expand Down

0 comments on commit d3fd92b

Please sign in to comment.