Django-filter: [2.1.0] BaseCSVWidget.value_from_datadict — AttributeError: el objeto 'list' no tiene el atributo 'split'

Creado en 12 jul. 2019  ·  15Comentarios  ·  Fuente: carltongibson/django-filter

me encontré con…

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'

y encontré que el código en...
https://github.com/carltongibson/django-filter/blob/1f47e36b614724a8735e0457fa511dcaf5448481/django_filters/widgets.py#L195 -L202
… no es robusto con respecto a super().value_from_datadict(data, files, name) devolviendo una lista. Necesito depurar esto más, pero tal vez ya sabes lo que está pasando aquí.

Comentario más útil

Acabo de tener este error y su explicación está enterrada en este hilo, así que para recapitular y ahorrar una larga búsqueda para futuros lectores:

Los campos ManyToMany declarados en los conjuntos de vistas filterset_fields no deben contener in

Una advertencia en tiempo de ejecución sería muy apreciada.

Todos 15 comentarios

Hola @moseb. ¿Puedes pegar el código FilterSet que estás usando? Las últimas líneas del seguimiento son suficientes para determinar por qué se produjo esta excepción.

Rastreo 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

Es de navegar por una lista donde django-rest-framework genera el código.

Hola @moseb. ¿Puedes pegar el código FilterSet que estás usando?

Para ser honesto, todavía no he encontrado ninguno en esa área del código. Podría ser todo por DRF. No estoy seguro todavía.
Soy consciente de que mi informe de error no es el ideal, pero es el mejor que tengo en este momento. ¡Lo siento!

Su vista de API debe tener filterset_class o filterset_fields , o si está usando una versión anterior de django-filter, filter_class y filter_fields .

Encontré el uso de filter_class , así que supongo que me ayudó a encontrar que la migración a django-filter 2.0 no se realizó correctamente hasta el final. ¡Frio!
¿Hay algo que impida que django-filter detecte los restos anteriores a 2.x y advierta sobre ellos, conceptualmente? ¿Avisa pero me lo perdí? ¿Sería una advertencia de código sobre estos una adición bienvenida para una solicitud de extracción?

v2.0 eliminó muchas de las advertencias de obsolescencia existentes, por lo que puede intentar ejecutar su conjunto de pruebas contra v1.0 y luego v1.1 de django-filter.

Suponiendo que esto se resolverá solo cuando actualice. Vuelve si no.

Hay una demostración reproducible del problema ahora en https://github.com/moseb/django-filter-issue-1103-demo. Considere volver a abrir este problema. ¡Gracias!

Ah, esto está relacionado con que ModelMultipleChoiceFilter no es compatible con la combinación de CSV, así como con la búsqueda in . Dos temas separados pero relacionados aquí.

No debería necesitar una búsqueda de in para campos m2m, ya que el filtro de opción múltiple ya proporciona un comportamiento similar.

¡Hola!

Ah, esto está relacionado con que ModelMultipleChoiceFilter no es compatible con la combinación de CSV, así como con la búsqueda in . Dos temas separados pero relacionados aquí.

¿Existen tickets en GitHub para estos problemas?

¿Parece que hacer BaseCSVWidget.value_from_datadict más robusto como se mencionó anteriormente por sí solo no solucionará el problema? (Todavía no entiendo completamente el problema).

No debería necesitar una búsqueda de in para campos m2m, ya que el filtro de opción múltiple ya proporciona un comportamiento similar.

in permite comparar múltiples valores por encima exact . Si no puedo usar ìn , ¿cómo compruebo si hay varios valores?

PD: ¿Podemos reabrir este ticket?

¿Parece que hacer BaseCSVWidget.value_from_datadict más robusto como se mencionó anteriormente por sí solo no solucionará el problema? (Todavía no entiendo completamente el problema).

Posiblemente. Si el valor ya es una lista, debería continuar y devolver esa lista. Dicho esto, no creo que tenga sentido mezclar el comportamiento CSV con el widget SelectMultiple .

in permite verificar múltiples valores sobre exact . Si no puedo usar ìn , ¿cómo compruebo si hay varios valores?

El ModelMultipleChoiceFilter construye una consulta OR a partir de objetos Q. Entonces, si tiene una cadena de consulta como /api/mymodel?m2m=a&m2m=b , terminaría con una llamada de filtro como

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

Con la versión 2.1, el lookup_expr ahora se aplica a cada objeto Q, por lo que terminaría con

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

Lo anterior no es válido y se rompería, sin embargo, tiene sentido para contains y otras búsquedas que están destinadas a trabajar con valores únicos.

Entonces, en resumen, todo lo que necesita hacer aquí es usar la búsqueda exact para su campo m2m.


Creo que el todo aquí es:

  • Haga que BaseCSVWidget sea ​​compatible con SelectMultiple devolviendo el valor si ya es una lista. O si no debería ser compatible, al menos deberíamos proporcionar un error útil en init.
  • Arreglar Meta.fields por m2m. Generar una búsqueda de $ in para ModelMultipleChoiceFilter tiene sentido. También deberíamos probar qué sucede con otras búsquedas como isnull , que espera un valor booleano.

Sí, esto todavía no parece un error , pero supongo que cómo funciona todo está poco documentado...

Acabo de tener este error y su explicación está enterrada en este hilo, así que para recapitular y ahorrar una larga búsqueda para futuros lectores:

Los campos ManyToMany declarados en los conjuntos de vistas filterset_fields no deben contener in

Una advertencia en tiempo de ejecución sería muy apreciada.

@marcosox Buena idea. ¡Feliz de ver un PR agregando eso!

¿Fue útil esta página
0 / 5 - 0 calificaciones