Add strictness setting

This commit is contained in:
Ryan P Kilby 2016-09-20 21:29:07 -04:00
parent f8a0d1a9cf
commit 67f7edfa23
8 changed files with 134 additions and 20 deletions

View File

@ -1,5 +1,6 @@
# flake8: noqa
from __future__ import absolute_import
from .constants import STRICTNESS
from .filterset import FilterSet
from .filters import *

View File

@ -3,6 +3,7 @@ from django.conf import settings as dj_settings
from django.core.signals import setting_changed
from django.utils.translation import ugettext_lazy as _
from .constants import STRICTNESS
from .utils import deprecate
@ -16,6 +17,8 @@ DEFAULTS = {
'NULL_CHOICE_LABEL': None,
'NULL_CHOICE_VALUE': 'null',
'STRICTNESS': STRICTNESS.RETURN_NO_RESULTS,
'VERBOSE_LOOKUPS': {
# transforms don't need to be verbose, since their expressions are chained
'date': _('date'),

View File

@ -0,0 +1,21 @@
ALL_FIELDS = '__all__'
class STRICTNESS:
class IGNORE:
pass
class RETURN_NO_RESULTS:
pass
class RAISE_VALIDATION_ERROR:
pass
# Values of False & True chosen for backward compatability reasons.
# Originally, these were the only options.
_LEGACY = {
False: IGNORE,
True: RETURN_NO_RESULTS,
"RAISE": RAISE_VALIDATION_ERROR,
}

View File

@ -10,10 +10,11 @@ from django.db import models
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields.related import ForeignObjectRel
from django.utils import six
from django.utils.text import capfirst
from django.utils.translation import ugettext as _
from .conf import settings
from .compat import remote_field, remote_queryset
from .constants import ALL_FIELDS, STRICTNESS
from .filters import (Filter, CharFilter, BooleanFilter, BaseInFilter, BaseRangeFilter,
ChoiceFilter, DateFilter, DateTimeFilter, TimeFilter, ModelChoiceFilter,
ModelMultipleChoiceFilter, NumberFilter, UUIDFilter,
@ -24,16 +25,6 @@ from .utils import try_dbfield, get_all_model_fields, get_model_field, resolve_f
ORDER_BY_FIELD = 'o'
class STRICTNESS(object):
"""
Values of False & True chosen for backward compatability reasons.
Originally, these were the only options.
"""
IGNORE = False
RETURN_NO_RESULTS = True
RAISE_VALIDATION_ERROR = "RAISE"
def get_declared_filters(bases, attrs, with_base_filters=True):
filters = []
for filter_name, obj in list(attrs.items()):
@ -62,10 +53,10 @@ def filters_for_model(model, fields=None, exclude=None, filter_for_field=None,
# Setting exclude with no fields implies all other fields.
if exclude is not None and fields is None:
fields = '__all__'
fields = ALL_FIELDS
# All implies all db fields associated with a filter_class.
if fields == '__all__':
if fields == ALL_FIELDS:
fields = get_all_model_fields(model)
# Loop through the list of fields.
@ -160,7 +151,7 @@ class FilterSetOptions(object):
self.order_by = getattr(options, 'order_by', False)
self.order_by_field = getattr(options, 'order_by_field', ORDER_BY_FIELD)
self.strict = getattr(options, 'strict', STRICTNESS.RETURN_NO_RESULTS)
self.strict = getattr(options, 'strict', None)
self.form = getattr(options, 'form', forms.Form)
@ -286,7 +277,14 @@ class BaseFilterSet(object):
self.form_prefix = prefix
# What to do on on validation errors
self.strict = self._meta.strict if strict is None else strict
# Fallback to meta, then settings strictness
if strict is None:
strict = self._meta.strict
if strict is None:
strict = settings.STRICTNESS
# transform legacy values
self.strict = STRICTNESS._LEGACY.get(strict, strict)
self.request = request
@ -326,7 +324,7 @@ class BaseFilterSet(object):
if not self.form.is_valid():
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise forms.ValidationError(self.form.errors)
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:
elif self.strict == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
@ -527,7 +525,7 @@ class FilterSet(six.with_metaclass(FilterSetMetaclass, BaseFilterSet)):
def filterset_factory(model):
meta = type(str('Meta'), (object,), {'model': model, 'fields': '__all__'})
meta = type(str('Meta'), (object,), {'model': model, 'fields': ALL_FIELDS})
filterset = type(str('%sFilterSet' % model._meta.object_name),
(FilterSet,), {'Meta': meta})
return filterset

View File

@ -89,3 +89,11 @@ For example, you could add verbose output for "exact" lookups.
'exact': 'is equal to',
})
return verbose_lookups
FILTERS_STRICTNESS
------------------
DEFAULT: ``STRICTNESS.RETURN_NO_RESULTS``
Set the global default for FilterSet :ref:`strictness <strict>`.

View File

@ -2,17 +2,23 @@
from django.test import TestCase, override_settings
from django_filters.conf import settings
from django_filters import FilterSet, STRICTNESS
from tests.models import User
class DefaultSettingsTests(TestCase):
def test_verbose_loookups(self):
def test_verbose_lookups(self):
self.assertIsInstance(settings.VERBOSE_LOOKUPS, dict)
self.assertIn('exact', settings.VERBOSE_LOOKUPS)
def test_disable_help_text(self):
self.assertFalse(settings.DISABLE_HELP_TEXT)
def test_strictness(self):
self.assertEqual(settings.STRICTNESS, STRICTNESS.RETURN_NO_RESULTS)
def test_help_text_filter(self):
self.assertTrue(settings.HELP_TEXT_FILTER)
@ -29,6 +35,44 @@ class DefaultSettingsTests(TestCase):
self.assertEqual(settings.NULL_CHOICE_VALUE, 'null')
class StrictnessTests(TestCase):
class F(FilterSet):
class Meta:
model = User
def test_settings_default(self):
self.assertEqual(self.F().strict, STRICTNESS.RETURN_NO_RESULTS)
def test_ignore(self):
with override_settings(FILTERS_STRICTNESS=STRICTNESS.IGNORE):
self.assertEqual(self.F().strict, STRICTNESS.IGNORE)
def test_return_no_results(self):
with override_settings(FILTERS_STRICTNESS=STRICTNESS.RETURN_NO_RESULTS):
self.assertEqual(self.F().strict, STRICTNESS.RETURN_NO_RESULTS)
def test_raise_validation_error(self):
with override_settings(FILTERS_STRICTNESS=STRICTNESS.RAISE_VALIDATION_ERROR):
self.assertEqual(self.F().strict, STRICTNESS.RAISE_VALIDATION_ERROR)
def test_legacy_ignore(self):
with override_settings(FILTERS_STRICTNESS=False):
self.assertEqual(self.F().strict, STRICTNESS.IGNORE)
def test_legacy_return_no_results(self):
with override_settings(FILTERS_STRICTNESS=True):
self.assertEqual(self.F().strict, STRICTNESS.RETURN_NO_RESULTS)
def test_legacy_raise_validation_error(self):
with override_settings(FILTERS_STRICTNESS='RAISE'):
self.assertEqual(self.F().strict, STRICTNESS.RAISE_VALIDATION_ERROR)
def test_legacy_differentiation(self):
self.assertNotEqual(STRICTNESS.IGNORE, False)
self.assertNotEqual(STRICTNESS.RETURN_NO_RESULTS, True)
self.assertNotEqual(STRICTNESS.RAISE_VALIDATION_ERROR, 'RAISE')
class OverrideSettingsTests(TestCase):
def test_attribute_override(self):

View File

@ -453,7 +453,6 @@ class DeprecatedOrderingFilterSetTests(TestCase):
order_by = ['status']
strict = STRICTNESS.IGNORE
self.assertFalse(F._meta.strict)
f = F({'o': 'username'}, queryset=self.qs)
self.assertQuerysetEqual(
f.qs, ['alex', 'jacob', 'aaron', 'carl'], lambda o: o.username)

View File

@ -5,8 +5,9 @@ import unittest
import django
from django.db import models
from django.test import TestCase
from django.test import TestCase, override_settings
from django_filters.constants import STRICTNESS
from django_filters.filterset import FilterSet
from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS
from django_filters.filters import Filter
@ -573,6 +574,45 @@ class FilterSetInstantiationTests(TestCase):
self.assertEqual(f.request, m)
class FilterSetStrictnessTests(TestCase):
def test_settings_default(self):
class F(FilterSet):
class Meta:
model = User
# Ensure default is not IGNORE
self.assertEqual(F().strict, STRICTNESS.RETURN_NO_RESULTS)
# override and test
with override_settings(FILTERS_STRICTNESS=STRICTNESS.IGNORE):
self.assertEqual(F().strict, STRICTNESS.IGNORE)
def test_meta_value(self):
class F(FilterSet):
class Meta:
model = User
strict = STRICTNESS.IGNORE
self.assertEqual(F().strict, STRICTNESS.IGNORE)
def test_init_default(self):
class F(FilterSet):
class Meta:
model = User
strict = STRICTNESS.IGNORE
strict = STRICTNESS.RAISE_VALIDATION_ERROR
self.assertEqual(F(strict=strict).strict, strict)
def test_legacy_value(self):
class F(FilterSet):
class Meta:
model = User
self.assertEqual(F(strict=False).strict, STRICTNESS.IGNORE)
class FilterSetTogetherTests(TestCase):
def setUp(self):