Django-filter: [2.1.0] BaseCSVWidget.value_from_datadict — AttributeError: объект 'list' не имеет атрибута 'split'

Созданный на 12 июл. 2019  ·  15Комментарии  ·  Источник: carltongibson/django-filter

Я столкнулся с…

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) возвращает список. Мне нужно отладить это еще, но, может быть, вы уже знаете, что здесь происходит?

Самый полезный комментарий

У меня только что была эта ошибка, и ее объяснение скрыто в этой ветке, так что подытожим и сохраним долгий поиск для будущих читателей:

Поля ManyToMany , объявленные в наборах представлений filterset_fields , не должны содержать in

Предупреждение во время выполнения было бы очень признательно.

Все 15 Комментарий

Привет @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 Хорошая идея. Приятно смотреть на пиар, добавляющий это!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги