group filter fields with meta option 'together'
This commit is contained in:
parent
13d4fe30e5
commit
d3c80b9d34
3
AUTHORS
3
AUTHORS
|
@ -18,4 +18,5 @@ Vladimir Sidorenko
|
||||||
Tom Christie
|
Tom Christie
|
||||||
Remco Wendt
|
Remco Wendt
|
||||||
Axel Haustant
|
Axel Haustant
|
||||||
Brad Erickson
|
Brad Erickson
|
||||||
|
Diogo Laginha
|
|
@ -141,6 +141,8 @@ class FilterSetOptions(object):
|
||||||
self.order_by = getattr(options, 'order_by', False)
|
self.order_by = getattr(options, 'order_by', False)
|
||||||
|
|
||||||
self.form = getattr(options, 'form', forms.Form)
|
self.form = getattr(options, 'form', forms.Form)
|
||||||
|
|
||||||
|
self.together = getattr(options, 'together', None)
|
||||||
|
|
||||||
|
|
||||||
class FilterSetMetaclass(type):
|
class FilterSetMetaclass(type):
|
||||||
|
@ -352,6 +354,22 @@ class BaseFilterSet(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def form(self):
|
def form(self):
|
||||||
|
|
||||||
|
def full_clean(form):
|
||||||
|
super(form.__class__, form).full_clean()
|
||||||
|
message = 'Following fields must be together: %s'
|
||||||
|
together = self._meta.together
|
||||||
|
cleaned_data = form.cleaned_data
|
||||||
|
if isinstance(together[0], (list, tuple)):
|
||||||
|
for each in together:
|
||||||
|
count = len([i for i in each if cleaned_data.get(i)])
|
||||||
|
if 0 < count < len(each):
|
||||||
|
return form.add_error(None, message % ','.join(each))
|
||||||
|
else:
|
||||||
|
count = len([i for i in together if cleaned_data.get(i)])
|
||||||
|
if 0 < count < len(together):
|
||||||
|
return form.add_error(None, message % ','.join(together))
|
||||||
|
|
||||||
if not hasattr(self, '_form'):
|
if not hasattr(self, '_form'):
|
||||||
fields = OrderedDict([
|
fields = OrderedDict([
|
||||||
(name, filter_.field)
|
(name, filter_.field)
|
||||||
|
@ -359,6 +377,8 @@ class BaseFilterSet(object):
|
||||||
fields[self.order_by_field] = self.ordering_field
|
fields[self.order_by_field] = self.ordering_field
|
||||||
Form = type(str('%sForm' % self.__class__.__name__),
|
Form = type(str('%sForm' % self.__class__.__name__),
|
||||||
(self._meta.form,), fields)
|
(self._meta.form,), fields)
|
||||||
|
if self._meta.together:
|
||||||
|
Form.full_clean = full_clean
|
||||||
if self.is_bound:
|
if self.is_bound:
|
||||||
self._form = Form(self.data, prefix=self.form_prefix)
|
self._form = Form(self.data, prefix=self.form_prefix)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -199,6 +199,24 @@ The inner ``Meta`` class also takes an optional ``form`` argument. This is a
|
||||||
form class from which ``FilterSet.form`` will subclass. This works similar to
|
form class from which ``FilterSet.form`` will subclass. This works similar to
|
||||||
the ``form`` option on a ``ModelAdmin.``
|
the ``form`` option on a ``ModelAdmin.``
|
||||||
|
|
||||||
|
Group fields with ``together``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The inner ``Meta`` class also takes an optional ``together`` argument. This
|
||||||
|
is a list of lists, each containing field names. For convenience can be a
|
||||||
|
single list/tuple when dealing with a single set of fields. Fields within a
|
||||||
|
field set must either be all or none present in the request for
|
||||||
|
``FilterSet.form`` to be valid.
|
||||||
|
|
||||||
|
import django_filters
|
||||||
|
|
||||||
|
class ProductFilter(django_filters.FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = Product
|
||||||
|
fields = ['price', 'release_date', 'rating']
|
||||||
|
together = ['rating', 'price']
|
||||||
|
|
||||||
|
|
||||||
Non-Meta options
|
Non-Meta options
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -16,7 +16,7 @@ if sys.argv[-1] == 'publish':
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-filter',
|
name='django-filter',
|
||||||
version='0.9.2',
|
version='0.9.3',
|
||||||
description=('Django-filter is a reusable Django application for allowing'
|
description=('Django-filter is a reusable Django application for allowing'
|
||||||
' users to filter querysets dynamically.'),
|
' users to filter querysets dynamically.'),
|
||||||
long_description=readme,
|
long_description=readme,
|
||||||
|
|
|
@ -628,3 +628,45 @@ class FilterSetOrderingTests(TestCase):
|
||||||
f = F({'o': 'status'}, queryset=self.qs)
|
f = F({'o': 'status'}, queryset=self.qs)
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
f.qs, ['carl', 'alex', 'aaron', 'jacob'], lambda o: o.username)
|
f.qs, ['carl', 'alex', 'aaron', 'jacob'], lambda o: o.username)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FilterSetTogetherTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.alex = User.objects.create(username='alex', status=1)
|
||||||
|
self.jacob = User.objects.create(username='jacob', status=2)
|
||||||
|
self.qs = User.objects.all().order_by('id')
|
||||||
|
|
||||||
|
def test_fields_set(self):
|
||||||
|
class F(FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['username', 'status', 'is_active', 'first_name']
|
||||||
|
together = [
|
||||||
|
('username', 'status'),
|
||||||
|
('first_name', 'is_active'),
|
||||||
|
]
|
||||||
|
|
||||||
|
f = F({}, queryset=self.qs)
|
||||||
|
self.assertEqual(f.qs.count(), 2)
|
||||||
|
f = F({'username': 'alex'}, queryset=self.qs)
|
||||||
|
self.assertEqual(f.qs.count(), 0)
|
||||||
|
f = F({'username': 'alex', 'status': 1}, queryset=self.qs)
|
||||||
|
self.assertEqual(f.qs.count(), 1)
|
||||||
|
self.assertQuerysetEqual(f.qs, [self.alex.pk], lambda o: o.pk)
|
||||||
|
|
||||||
|
def test_single_fields_set(self):
|
||||||
|
class F(FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['username', 'status']
|
||||||
|
together = ['username', 'status']
|
||||||
|
|
||||||
|
f = F({}, queryset=self.qs)
|
||||||
|
self.assertEqual(f.qs.count(), 2)
|
||||||
|
f = F({'username': 'alex'}, queryset=self.qs)
|
||||||
|
self.assertEqual(f.qs.count(), 0)
|
||||||
|
f = F({'username': 'alex', 'status': 1}, queryset=self.qs)
|
||||||
|
self.assertEqual(f.qs.count(), 1)
|
||||||
|
self.assertQuerysetEqual(f.qs, [self.alex.pk], lambda o: o.pk)
|
||||||
|
|
Loading…
Reference in New Issue