-
Notifications
You must be signed in to change notification settings - Fork 770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FilterMethod.__call__ calling method
with value=<Queryset []>
#1550
Comments
Further study about Django forms: Setupfrom django import forms
from django.contrib.auth import get_user_model
User = get_user_model()
class MyForm(forms.Form):
users = forms.ModelMultipleChoiceField(queryset=User.objects.all(), required=False) Empty formform = MyForm(data={"users":[]})
form.is_valid()
print(form.cleaned_data) # {'users': <Queryset []>} Non-empty formform = MyForm(data={"users":[1]})
form.is_valid()
print(form.cleaned_data) # {'users': <Queryset [<User: [email protected]>]>} It appears that vanilla Django forms return Queryset instances for the ModelMultipleChoiceField. So that leaves the question of why does The metaclass Setup, but with django-filter fieldsfrom django import forms
from django.contrib.auth import get_user_model
from django_filters import fields as django_filter_fields
User = get_user_model()
class MyForm(forms.Form):
users = django_filter_fields.ModelMultipleChoiceField(queryset=User.objects.all(), required=False) Empty formform = MyForm(data={"users":[]})
form.is_valid()
print(form.validated_data) # dict_values([<QuerySet []>]) Non-empty formform = MyForm(data={"users":[1]})
form.is_valid()
print(form.validated_data) # dict_values([[<User: [email protected]>]]) There, I found it. A minimal case demonstrating the difference in |
I think I found out where the problem is. In django.forms.models.py:ModelMultipleChoiceField.clean it has def clean(self, value):
value = self.prepare_value(value)
if self.required and not value:
raise ValidationError(self.error_messages["required"], code="required")
elif not self.required and not value:
return self.queryset.none()
qs = self._check_values(value)
self.run_validators(value)
return qs The two return paths are either The former is where the empty queryset comes from.
def _check_values(self, value):
...
result = list(super()._check_values(value)) # This is where the list vs queryset comes from
result += [self.null_value] if null else []
return result we can clearly see the Solutionclass ModelMultipleChoiceFilter(QuerySetRequestMixin, MultipleChoiceFilter):
field_class = ModelMultipleChoiceField
def clean(self, value):
# When a 'method' argument is passed, the proxy FilterMethod class is used
# and first checks if the value is in EMPTY_VALUES, calling 'method' only if it is not.
# When value is empty, super() returns an empty queryset. `value in EMPTY_VALUES is False`.
# When value is not empty, super() returns a list. `value in EMPTY_VALUES is True`.
#
# The inconsistency is fixed by calling list() on whatever super() returns. That way
# FilterMethod will always get a list and `value in EMPTY_VALUES` will work as intended.
return list(super().clean(value)) |
Here's my filterset:
If I GET
/api/locks/
,/api/locks/?
, or/api/locks/?grate_widths=
myfilter_grate_widths
method is called. Thevalue
is<Queryset []>
.Examining the call stack we see that it came from
EMPTY_VALUES is
([], (), {}, "", None)
.But of course the issue is that
value
is<Queryset []>
, which is not inEMPTY_VALUES
.I did some digging and this value is generated by the form. The form is django.forms.Form looking at the metaclass code. So I am not sure if this bug is django-filter or django itself.
Yes I can fix this by adding
if not value: return queryset
, but the source code inFilteMethod
suggests that my method should only be called when there is some non-empty value, which my url parameters are (empty).What am I using
The text was updated successfully, but these errors were encountered: