Last active
December 15, 2015 14:39
-
-
Save nkryptic/5275724 to your computer and use it in GitHub Desktop.
different variations for django-filter's qs method. all require the new 'strict' attribute for the FilterSet class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all implementations ensure that: | |
non-empty querysets are always ordered. either by submitted value when bound, | |
the order field's initial value or the value of the first ordering choice | |
when filterset is strict and bound: | |
* the form is validated, so errors are available | |
* returns empty queryset when form is invalid | |
* returns fully filtered queryset otherwise | |
when filterset is strict and unbound: | |
* the form is not validated | |
* returns empty queryset when form *would* be invalid, based on initial values | |
* returns fully filtered queryset otherwise | |
when filterset is forgiving and bound: | |
* the form is validated, so errors are available | |
* returns queryset filtered from valid fields | |
when filterset is forgiving and unbound: | |
* the form is not validated | |
* returns queryset filtered from fields that *would* be valid, based on initial values |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This version is the simplest, at the expense of calling is_valid on the form | |
when 'bound' and also cleaning each field a second time. | |
""" | |
@property | |
def qs(self): | |
if not hasattr(self, '_qs'): | |
if self.is_bound: | |
if not self.form.is_valid() and self.strict: | |
self._qs = self.queryset.none() | |
return self._qs | |
qs = self.queryset.all() | |
for name, filter_ in six.iteritems(self.filters): | |
data = self.form[name].value() | |
try: | |
value = self.form.fields[name].clean(data) | |
qs = filter_.filter(qs, value) | |
except forms.ValidationError: | |
if self.strict: | |
self._qs = self.queryset.none() | |
return self._qs | |
if self._meta.order_by: | |
value = None | |
order_field = self.form.fields[self.order_by_field] | |
data = self.form[self.order_by_field].value() | |
try: | |
value = order_field.clean(data) | |
except forms.ValidationError: | |
if self.strict: | |
self._qs = self.queryset.none() | |
return self._qs | |
if not value: | |
value = order_field.choices[0][0] | |
qs = qs.order_by(value) | |
self._qs = qs | |
return self._qs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This version is the same as the first, but uses the form's cleaned_data when available. | |
""" | |
@property | |
def qs(self): | |
if not hasattr(self, '_qs'): | |
valid = self.is_bound and self.form.is_valid() | |
if self.strict and self.is_bound and not valid: | |
self._qs = self.queryset.none() | |
return self._qs | |
sentinel = () | |
def get_value(name): | |
value = sentinel | |
if valid: | |
value = self.form.cleaned_data[name] | |
else: | |
data = self.form[name].value() | |
try: | |
value = self.form.fields[name].clean(data) | |
except forms.ValidationError: | |
pass | |
return value | |
qs = self.queryset.all() | |
for name, filter_ in six.iteritems(self.filters): | |
value = get_value(name) | |
if value is sentinel: | |
if self.strict: | |
self._qs = self.queryset.none() | |
return self._qs | |
else: | |
qs = filter_.filter(qs, value) | |
if self._meta.order_by: | |
value = get_value(self.order_by_field) | |
if value is sentinel: | |
if self.strict: | |
self._qs = self.queryset.none() | |
return self._qs | |
if not value: | |
value = self.form.fields[self.order_by_field].choices[0][0] | |
qs = qs.order_by(value) | |
self._qs = qs | |
return self._qs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This version uses a 'validation-only' bound form, when the filterset is unbound. | |
But, it relies on Django 1.5's cleaned_data support when the form is invalid, so | |
it can't be used unless we drop 1.4 compatibility. | |
""" | |
def get_form_class(self): | |
fields = SortedDict([ | |
(name, filter_.field) | |
for name, filter_ in six.iteritems(self.filters)]) | |
fields[self.order_by_field] = self.ordering_field | |
Form = type(str('%sForm' % self.__class__.__name__), | |
(self._meta.form,), fields) | |
return Form | |
@property | |
def form(self): | |
if not hasattr(self, '_form'): | |
Form = self.get_form_class() | |
if self.is_bound: | |
self._form = Form(self.data, prefix=self.form_prefix) | |
else: | |
self._form = Form(prefix=self.form_prefix) | |
return self._form | |
@property | |
def qs(self): | |
if not hasattr(self, '_qs'): | |
if self.is_bound: | |
form = self.form | |
else: | |
Form = self.get_form_class() | |
data = dict([(x, y.initial) for x, y in Form.base_fields.items()]) | |
form = Form(data, prefix=self.form_prefix) | |
if form.is_valid() or not self.strict: | |
qs = self.queryset.all() | |
data = form.cleaned_data | |
for name, filter_ in six.iteritems(self.filters): | |
if name in data: | |
qs = filter_.filter(qs, data[name]) | |
if self._meta.order_by: | |
value = None | |
if self.order_by_field in data: | |
value = data[self.order_by_field] | |
if not value: | |
value = form.fields[self.order_by_field].choices[0][0] | |
qs = qs.order_by(value) | |
else: | |
qs = self.queryset.none() | |
self._qs = qs | |
return self._qs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment