summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarlton Gibson <carlton.gibson@noumenal.co.uk>2016-11-08 10:02:13 (GMT)
committerGitHub <noreply@github.com>2016-11-08 10:02:13 (GMT)
commite6bafd58fd150ce02d339b0a85ebfaa116866a6f (patch)
tree34bc7b1b6fc7b3ffca461fbb3db9d68aff6c692d
parent470b89c1a1741d16f54cf9487e68bc115acdfd6c (diff)
parent703172c29fcf838762427ea331e6d933a083961d (diff)
downloaddjango-filter-e6bafd58fd150ce02d339b0a85ebfaa116866a6f.zip
django-filter-e6bafd58fd150ce02d339b0a85ebfaa116866a6f.tar.gz
django-filter-e6bafd58fd150ce02d339b0a85ebfaa116866a6f.tar.bz2
Merge pull request #548 from pySilver/feature/multiple-values-input-format
Add `QueryArrayWidget`
-rw-r--r--django_filters/widgets.py36
-rw-r--r--tests/test_widgets.py73
2 files changed, 108 insertions, 1 deletions
diff --git a/django_filters/widgets.py b/django_filters/widgets.py
index 7127306..1b6b230 100644
--- a/django_filters/widgets.py
+++ b/django_filters/widgets.py
@@ -11,6 +11,7 @@ except:
from django import forms
from django.db.models.fields import BLANK_CHOICE_DASH
from django.forms.widgets import flatatt
+from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.six import string_types
@@ -171,3 +172,38 @@ class BaseCSVWidget(forms.Widget):
class CSVWidget(BaseCSVWidget, forms.TextInput):
pass
+
+
+class QueryArrayWidget(BaseCSVWidget, forms.TextInput):
+ """
+ Enables request query array notation that might be consumed by MultipleChoiceFilter
+
+ 1. Values can be provided as csv string: ?foo=bar,baz
+ 2. Values can be provided as query array: ?foo[]=bar&foo[]=baz
+ 3. Values can be provided as query array: ?foo=bar&foo=baz
+
+ Note: Duplicate and empty values are skipped from results
+ """
+
+ def value_from_datadict(self, data, files, name):
+ if not isinstance(data, MultiValueDict):
+ data = MultiValueDict(data)
+
+ values_list = data.getlist(name, data.getlist('%s[]' % name)) or []
+
+ if isinstance(values_list, string_types):
+ values_list = [values_list]
+
+ # apparently its an array, so no need to process it's values as csv
+ # ?foo=1&foo=2 -> data.getlist(foo) -> foo = [1, 2]
+ # ?foo[]=1&foo[]=2 -> data.getlist(foo[]) -> foo = [1, 2]
+ if len(values_list) > 1:
+ ret = [x for x in values_list if x]
+ elif len(values_list) == 1:
+ # treat first element as csv string
+ # ?foo=1,2 -> data.getlist(foo) -> foo = ['1,2']
+ ret = [x.strip() for x in values_list[0].rstrip(',').split(',') if x]
+ else:
+ ret = []
+
+ return list(set(ret))
diff --git a/tests/test_widgets.py b/tests/test_widgets.py
index 2856cb1..1d9d3e8 100644
--- a/tests/test_widgets.py
+++ b/tests/test_widgets.py
@@ -4,7 +4,7 @@ from __future__ import unicode_literals
from django.test import TestCase
from django.forms import TextInput, Select
-from django_filters.widgets import BooleanWidget
+from django_filters.widgets import BooleanWidget, QueryArrayWidget
from django_filters.widgets import BaseCSVWidget
from django_filters.widgets import CSVWidget
from django_filters.widgets import RangeWidget
@@ -279,3 +279,74 @@ class CSVSelectTests(TestCase):
self.assertHTMLEqual(w.render('price', [1, 2]), """
<input type="text" name="price" value="1,2" />""")
+
+
+class QueryArrayWidgetTests(TestCase):
+
+ def test_widget_value_from_datadict(self):
+ w = QueryArrayWidget()
+
+ # Values can be provided as csv string: ?foo=bar,baz
+ data = {'price': None}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, [])
+
+ data = {'price': '1'}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, ['1'])
+
+ data = {'price': '1,2'}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(sorted(result), ['1', '2'])
+
+ data = {'price': '1,,2'}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(sorted(result), ['1', '2'])
+
+ data = {'price': '1,'}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, ['1'])
+
+ data = {'price': ','}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, [])
+
+ data = {'price': ''}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, [])
+
+ result = w.value_from_datadict({}, {}, 'price')
+ self.assertEqual(result, [])
+
+ # Values can be provided as query array: ?foo[]=bar&foo[]=baz
+
+ data = {'price[]': None}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, [])
+
+ data = {'price[]': ['1']}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, ['1'])
+
+ data = {'price[]': ['1', '2']}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(sorted(result), ['1', '2'])
+
+ data = {'price[]': ['1', '', '2']}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(sorted(result), ['1', '2'])
+
+ data = {'price[]': ['1', '']}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, ['1'])
+
+ data = {'price[]': ['', '']}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, [])
+
+ data = {'price[]': []}
+ result = w.value_from_datadict(data, {}, 'price')
+ self.assertEqual(result, [])
+
+ result = w.value_from_datadict({}, {}, 'price')
+ self.assertEqual(result, [])