Merge pull request #548 from pySilver/feature/multiple-values-input-format

Add `QueryArrayWidget`
This commit is contained in:
Carlton Gibson 2016-11-08 11:02:13 +01:00 committed by GitHub
commit e6bafd58fd
2 changed files with 108 additions and 1 deletions

View File

@ -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))

View File

@ -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, [])