Django-filter: [2.1.0] BaseCSVWidget.value_from_datadict — AttributeError: objeto 'list' não tem atributo 'split'

Criado em 12 jul. 2019  ·  15Comentários  ·  Fonte: carltongibson/django-filter

me deparei com…

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'

e descobri que o código em …
https://github.com/carltongibson/django-filter/blob/1f47e36b614724a8735e0457fa511dcaf5448481/django_filters/widgets.py#L195 -L202
… não é robusto em relação a super().value_from_datadict(data, files, name) retornar uma lista. Eu preciso depurar isso mais, mas talvez você já saiba o que está acontecendo aqui?

Comentários muito úteis

Acabei de ter esse erro e sua explicação está enterrada neste tópico, para recapitular e salvar uma longa pesquisa para futuros leitores:

Campos ManyToMany declarados em viewsets filterset_fields não devem conter in

Um aviso em tempo de execução seria muito apreciado.

Todos 15 comentários

Olá @moseb. Você pode colar o código do FilterSet que está usando? As últimas linhas do traceback são suficientes para determinar por que você atingiu essa exceção.

Retorno completo:

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

É de navegar em uma lista onde o django-rest-framework gera o código.

Olá @moseb. Você pode colar o código do FilterSet que está usando?

Para ser honesto, ainda não encontrei nenhum nessa área do código. Pode ser tudo por DRF. Não tenho certeza ainda.
Estou ciente de que meu relatório de bug não é o ideal, mas é o melhor que tenho no momento. Desculpe!

Sua visualização de API deve ter filterset_class ou filterset_fields , ou se você estiver usando uma versão mais antiga do django-filter, filter_class e filter_fields .

Eu encontrei o uso de filter_class — então acho que você acabou de me ajudar a descobrir que a migração para o django-filter 2.0 não foi feita corretamente até o fim. Legal!
Existe algo impedindo o django-filter de detectar sobras pré-2.xe alertar sobre elas, conceitualmente? Ele avisa, mas eu perdi? O código de alerta sobre isso seria uma adição bem-vinda para uma solicitação pull?

A v2.0 removeu muitos dos avisos de depreciação existentes, então você pode tentar executar seu conjunto de testes na v1.0 e depois na v1.1 do django-filter.

Supondo que isso se resolva quando você atualizar. Volte se não.

Há uma demonstração reproduzível do problema agora em https://github.com/moseb/django-filter-issue-1103-demo. Considere reabrir este problema. Obrigada!

Ah, isso está relacionado ao ModelMultipleChoiceFilter não ser compatível com o mixin CSV, assim como a pesquisa in . Duas questões separadas, mas relacionadas aqui.

Você não deve precisar de uma pesquisa de in para campos m2m, pois o filtro de múltipla escolha já oferece um comportamento semelhante.

Oi!

Ah, isso está relacionado ao ModelMultipleChoiceFilter não ser compatível com o mixin CSV, assim como a pesquisa in . Duas questões separadas, mas relacionadas aqui.

Existem tickets existentes no GitHub para esses problemas?

Parece que tornar BaseCSVWidget.value_from_datadict mais robusto, como mencionado acima, não resolverá o problema? (Ainda não entendi completamente o problema.)

Você não deve precisar de uma pesquisa de in para campos m2m, pois o filtro de múltipla escolha já oferece um comportamento semelhante.

in permite verificar vários valores acima exact . Se eu não puder usar ìn , como faço para verificar vários valores?

PS: Podemos reabrir este ticket?

Parece que tornar BaseCSVWidget.value_from_datadict mais robusto, como mencionado acima, não resolverá o problema? (Ainda não entendi completamente o problema.)

Possivelmente. Se o valor já for uma lista, ele deve ir em frente e retornar essa lista. Dito isso, não acho que faça sentido misturar o comportamento CSV com o widget SelectMultiple .

in permite verificar vários valores acima exact . Se eu não puder usar ìn , como faço para verificar vários valores?

O ModelMultipleChoiceFilter constrói uma consulta OR a partir de objetos Q. Portanto, se você tiver uma string de consulta como /api/mymodel?m2m=a&m2m=b , acabará com uma chamada de filtro como

MyModel.objects.filter(Q(m2m='a') | Q(m2m='b'))

Com a versão 2.1, o lookup_expr agora é aplicado a cada objeto Q, então você acabaria com

MyModel.objects.filter(Q(m2m__in='a') | Q(m2m__in='b'))

O acima é inválido e não funcionaria, mas faz sentido para contains e outras pesquisas que se destinam a funcionar com valores únicos.

Então, resumindo, tudo que você precisa fazer aqui é usar a pesquisa exact para o seu campo m2m.


Acho que o todo aqui é:

  • Torne BaseCSVWidget compatível com SelectMultiple retornando o valor se já for uma lista. Ou se não for compatível, devemos pelo menos fornecer um erro útil no init.
  • Corrija Meta.fields para m2m. Gerar uma pesquisa de in para ModelMultipleChoiceFilter não faz sentido. Também devemos testar o que acontece para outras pesquisas como isnull , que espera um booleano.

Sim, isso ainda não parece um bug , mas como tudo funciona está documentado, eu acho ...

Acabei de ter esse erro e sua explicação está enterrada neste tópico, para recapitular e salvar uma longa pesquisa para futuros leitores:

Campos ManyToMany declarados em viewsets filterset_fields não devem conter in

Um aviso em tempo de execução seria muito apreciado.

@marcosox Boa ideia. Feliz em ver um PR acrescentando isso!

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

sassanh picture sassanh  ·  4Comentários

gotexis picture gotexis  ·  4Comentários

chromakey picture chromakey  ·  5Comentários

xtrinch picture xtrinch  ·  4Comentários

loganknecht picture loganknecht  ·  4Comentários