Я столкнулся с…
Traceback (most recent call last):
[..]
File "/usr/local/lib/python3.7/site-packages/django/forms/forms.py", line 393, in _clean_fields
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
File "/usr/local/lib/python3.7/site-packages/django_filters/widgets.py", line 201, in value_from_datadict
return value.split(',')
AttributeError: 'list' object has no attribute 'split'
и обнаружил, что код в …
https://github.com/carltongibson/django-filter/blob/1f47e36b614724a8735e0457fa511dcaf5448481/django_filters/widgets.py#L195 -L202
… не является надежным в отношении того, что super().value_from_datadict(data, files, name)
возвращает список. Мне нужно отладить это еще, но, может быть, вы уже знаете, что здесь происходит?
Привет @moseb. Можете ли вы вставить код FilterSet, который вы используете? Последних нескольких строк обратной трассировки достаточно, чтобы определить, почему вы столкнулись с этим исключением.
Полная трассировка:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/wsgiref/handlers.py", line 137, in run
self.result = application(self.environ, self.start_response)
File "/usr/local/lib/python3.7/site-packages/django/contrib/staticfiles/handlers.py", line 65, in __call__
return self.application(environ, start_response)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/wsgi.py", line 141, in __call__
response = self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 75, in get_response
response = self._middleware_chain(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py", line 94, in __call__
response = response or self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py", line 94, in __call__
response = response or self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py", line 94, in __call__
response = response or self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py", line 94, in __call__
response = response or self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py", line 94, in __call__
response = response or self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/utils/deprecation.py", line 94, in __call__
response = response or self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django_global_request/middleware.py", line 15, in __call__
return self.get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 36, in inner
response = response_for_exception(request, exc)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 90, in response_for_exception
response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 145, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.7/site-packages/django/core/handlers/base.py", line 143, in _get_response
response = response.render()
File "/usr/local/lib/python3.7/site-packages/django/template/response.py", line 106, in render
self.content = self.rendered_content
File "/usr/local/lib/python3.7/site-packages/rest_framework/response.py", line 72, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context)
File "/usr/local/lib/python3.7/site-packages/rest_framework/renderers.py", line 733, in render
context = self.get_context(data, accepted_media_type, renderer_context)
File "/usr/local/lib/python3.7/site-packages/rest_framework/renderers.py", line 710, in get_context
'filter_form': self.get_filter_form(data, view, request),
File "/usr/local/lib/python3.7/site-packages/rest_framework/renderers.py", line 642, in get_filter_form
html = backend().to_html(request, queryset, view)
File "/usr/local/lib/python3.7/site-packages/rest_framework_filters/backends.py", line 52, in to_html
return super().to_html(request, queryset, view)
File "/usr/local/lib/python3.7/site-packages/django_filters/rest_framework/backends.py", line 105, in to_html
return template.render(context, request)
File "/usr/local/lib/python3.7/site-packages/django/template/backends/django.py", line 61, in render
return self.template.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 171, in render
return self._render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
return self.nodelist.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 937, in render
bit = node.render_annotated(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
return self.render(context)
File "/usr/local/lib/python3.7/site-packages/crispy_forms/templatetags/crispy_forms_tags.py", line 199, in render
c = self.get_render(context).flatten()
File "/usr/local/lib/python3.7/site-packages/crispy_forms/templatetags/crispy_forms_tags.py", line 118, in get_render
actual_form.form_html = helper.render_layout(actual_form, node_context, template_pack=self.template_pack)
File "/usr/local/lib/python3.7/site-packages/crispy_forms/helper.py", line 308, in render_layout
template_pack=template_pack
File "/usr/local/lib/python3.7/site-packages/crispy_forms/layout.py", line 140, in render
return self.get_rendered_fields(form, form_style, context, template_pack, **kwargs)
File "/usr/local/lib/python3.7/site-packages/crispy_forms/layout.py", line 104, in get_rendered_fields
for field in self.fields
File "/usr/local/lib/python3.7/site-packages/crispy_forms/layout.py", line 104, in <genexpr>
for field in self.fields
File "/usr/local/lib/python3.7/site-packages/crispy_forms/utils.py", line 148, in render_field
html = template.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/backends/django.py", line 61, in render
return self.template.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 171, in render
return self._render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 163, in _render
return self.nodelist.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 937, in render
bit = node.render_annotated(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
return self.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/defaulttags.py", line 309, in render
return nodelist.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 937, in render
bit = node.render_annotated(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
return self.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/defaulttags.py", line 309, in render
return nodelist.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 937, in render
bit = node.render_annotated(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 904, in render_annotated
return self.render(context)
File "/usr/local/lib/python3.7/site-packages/django/template/defaulttags.py", line 302, in render
match = condition.eval(context)
File "/usr/local/lib/python3.7/site-packages/django/template/defaulttags.py", line 876, in eval
return self.value.resolve(context, ignore_failures=True)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 671, in resolve
obj = self.var.resolve(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 796, in resolve
value = self._resolve_lookup(context)
File "/usr/local/lib/python3.7/site-packages/django/template/base.py", line 837, in _resolve_lookup
current = getattr(current, bit)
File "/usr/local/lib/python3.7/site-packages/django/forms/boundfield.py", line 74, in errors
return self.form.errors.get(self.name, self.form.error_class())
File "/usr/local/lib/python3.7/site-packages/django/forms/forms.py", line 180, in errors
self.full_clean()
File "/usr/local/lib/python3.7/site-packages/django/forms/forms.py", line 381, in full_clean
self._clean_fields()
File "/usr/local/lib/python3.7/site-packages/django/forms/forms.py", line 393, in _clean_fields
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
File "/usr/local/lib/python3.7/site-packages/django_filters/widgets.py", line 201, in value_from_datadict
return value.split(',')
AttributeError: 'list' object has no attribute 'split'
[12/Jul/2019 13:49:06] "GET /XXXXXXXX/?ordering=first_name HTTP/1.1" 500 59
Это при просмотре списка, где django-rest-framework генерирует код.
Привет @moseb. Можете ли вы вставить код FilterSet, который вы используете?
Честно говоря, я еще не нашел ничего в этой области кода. Это может быть все DRF. Пока не уверен.
Я знаю, что мой отчет об ошибках не идеален, но это лучшее, что у меня есть на данный момент. Прости!
Ваше представление API должно иметь либо filterset_class
, либо filterset_fields
, либо, если вы используете более старую версию django-filter, filter_class
и filter_fields
.
Я нашел применение filter_class
— думаю, вы только что помогли мне обнаружить, что миграция на django-filter 2.0 не была выполнена должным образом до конца. Прохладный!
Есть ли что-то, что мешает django-filter обнаруживать остатки до 2.x и предупреждать о них концептуально? Предупреждает, но я пропустил? Будет ли предупреждение кода об этом желанным дополнением для запроса на вытягивание?
Версия 2.0 удалила множество существующих предупреждений об устаревании, поэтому вы можете попробовать запустить свой набор тестов для версии 1.0, а затем для версии 1.1 django-filter.
Предполагая, что это разрешится само собой при обновлении. Вернись, если нет.
Теперь воспроизводимая демонстрация проблемы доступна по адресу https://github.com/moseb/django-filter-issue-1103-demo. Пожалуйста, рассмотрите возможность повторного открытия этого вопроса. Спасибо!
Ах, это связано с тем, что ModelMultipleChoiceFilter
не совместим с миксином CSV, а также с поиском in
. Здесь два отдельных, но связанных вопроса.
Вам не нужен поиск in
для полей m2m, поскольку фильтр множественного выбора уже обеспечивает аналогичное поведение.
Привет!
Ах, это связано с тем, что
ModelMultipleChoiceFilter
не совместим с миксином CSV, а также с поискомin
. Здесь два отдельных, но связанных вопроса.
Есть ли существующие билеты на GitHub для этих проблем?
Похоже, что повышение надежности BaseCSVWidget.value_from_datadict
, как уже упоминалось выше, само по себе не решит проблему? (Я еще не до конца разобрался в вопросе.)
Вам не нужен поиск
in
для полей m2m, поскольку фильтр множественного выбора уже обеспечивает аналогичное поведение.
in
позволяет проверять несколько значений свыше exact
. Если я не могу использовать ìn
, как мне проверить наличие нескольких значений?
PS: Можем ли мы повторно открыть этот билет?
Похоже, что повышение надежности
BaseCSVWidget.value_from_datadict
, как уже упоминалось выше, само по себе не решит проблему? (Я еще не до конца разобрался в вопросе.)
Возможно. Если значение уже является списком, оно должно просто вернуться и вернуть этот список. Тем не менее, я не думаю, что имеет смысл смешивать поведение CSV с виджетом SelectMultiple
.
in
позволяет проверять несколько значений свышеexact
. Если я не могу использоватьìn
, как мне проверить наличие нескольких значений?
ModelMultipleChoiceFilter
строит запрос ИЛИ из объектов Q. Итак, если у вас есть строка запроса, например /api/mymodel?m2m=a&m2m=b
, вы получите вызов фильтра, например
MyModel.objects.filter(Q(m2m='a') | Q(m2m='b'))
В выпуске 2.1 lookup_expr
теперь применяется к каждому объекту Q, поэтому в итоге вы получите
MyModel.objects.filter(Q(m2m__in='a') | Q(m2m__in='b'))
Приведенное выше недействительно и может привести к поломке, однако это имеет смысл для contains
и других операций поиска, предназначенных для работы с одиночными значениями.
Короче говоря, все, что вам нужно сделать, это использовать поиск exact
для вашего поля m2m.
Я думаю, что задача здесь:
BaseCSVWidget
совместимым с SelectMultiple
, вернув значение, если это уже список. Или, если это не должно быть совместимо, мы должны, по крайней мере, предоставить полезную ошибку при инициализации.Meta.fields
для m2m. Создание поиска in
для ModelMultipleChoiceFilter
бессмысленно. Мы также должны проверить, что происходит для других поисков, таких как isnull
, которые ожидают логическое значение.Да, это все еще не похоже на ошибку , но то, как все это работает, я думаю, недостаточно задокументировано...
У меня только что была эта ошибка, и ее объяснение скрыто в этой ветке, так что подытожим и сохраним долгий поиск для будущих читателей:
Поля ManyToMany
, объявленные в наборах представлений filterset_fields
, не должны содержать in
Предупреждение во время выполнения было бы очень признательно.
@marcosox Хорошая идея. Приятно смотреть на пиар, добавляющий это!
Самый полезный комментарий
У меня только что была эта ошибка, и ее объяснение скрыто в этой ветке, так что подытожим и сохраним долгий поиск для будущих читателей:
Поля
ManyToMany
, объявленные в наборах представленийfilterset_fields
, не должны содержатьin
Предупреждение во время выполнения было бы очень признательно.