Django-filter: [2.1.0] BaseCSVWidget.value_from_datadict — AttributeError: Objekt „list“ hat kein Attribut „split“

Erstellt am 12. Juli 2019  ·  15Kommentare  ·  Quelle: carltongibson/django-filter

Ich bin ... über den Weg gelaufen …

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'

und festgestellt, dass der Code bei …
https://github.com/carltongibson/django-filter/blob/1f47e36b614724a8735e0457fa511dcaf5448481/django_filters/widgets.py#L195 -L202
… ist nicht robust gegenüber der Rückgabe einer Liste durch super().value_from_datadict(data, files, name) . Ich muss das mehr debuggen, aber vielleicht weißt du schon, was hier vor sich geht?

Hilfreichster Kommentar

Ich hatte gerade diesen Fehler und seine Erklärung ist in diesem Thread begraben, um es zusammenzufassen und zukünftigen Lesern eine lange Suche zu ersparen:

ManyToMany -Felder, die in filterset_fields der Viewsets deklariert sind, dürfen nicht in enthalten

Eine Warnung zur Laufzeit wäre sehr wünschenswert.

Alle 15 Kommentare

Hallo @moseb. Können Sie den von Ihnen verwendeten FilterSet-Code einfügen? Die letzten paar Zeilen der Rückverfolgung reichen aus, um festzustellen, warum diese Ausnahme aufgetreten ist.

Vollständige Rückverfolgung:

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 stammt aus dem Durchsuchen einer Liste, in der django-rest-framework den Code generiert.

Hallo @moseb. Können Sie den von Ihnen verwendeten FilterSet-Code einfügen?

Um ehrlich zu sein, habe ich in diesem Bereich des Codes noch keine gefunden. Es könnte alles von DRF sein. Noch nicht sicher.
Ich bin mir bewusst, dass mein Fehlerbericht nicht ideal ist, aber er ist der beste, den ich im Moment habe. Verzeihung!

Ihre API-Ansicht sollte entweder filterset_class oder filterset_fields haben, oder wenn Sie eine ältere Version von django-filter verwenden, filter_class und filter_fields .

Ich habe Verwendung von filter_class gefunden – also schätze ich, dass Sie mir gerade dabei geholfen haben, herauszufinden, dass die Migration zu django-filter 2.0 nicht richtig bis zu ihrem Ende durchgeführt wurde. Cool!
Gibt es etwas, das django-filter daran hindert, Pre-2.x-Reste zu erkennen und konzeptionell vor ihnen zu warnen? Warnt es, aber ich habe es verpasst? Wäre eine Code-Warnung darüber eine willkommene Ergänzung für einen Pull-Request?

v2.0 hat viele der bestehenden Verfallswarnungen entfernt, also könnten Sie versuchen, Ihre Testsuite gegen v1.0 und dann gegen v1.1 von django-filter auszuführen.

Angenommen, dies löst sich beim Update von selbst. Kommen Sie zurück, wenn nicht.

Es gibt jetzt eine reproduzierbare Demo des Problems unter https://github.com/moseb/django-filter-issue-1103-demo. Bitte erwägen Sie, dieses Thema erneut zu öffnen. Danke schön!

Ah, das hängt damit zusammen, dass ModelMultipleChoiceFilter nicht mit dem CSV-Mixin kompatibel ist, sowie mit der in -Suche. Zwei separate, aber verwandte Probleme hier.

Sie sollten keine in -Suche für m2m-Felder benötigen, da der Multiple-Choice-Filter bereits ein ähnliches Verhalten bietet.

Hallo!

Ah, das hängt damit zusammen, dass ModelMultipleChoiceFilter nicht mit dem CSV-Mixin kompatibel ist, sowie mit der in -Suche. Zwei separate, aber verwandte Probleme hier.

Gibt es für diese Probleme bestehende Tickets auf GitHub?

Es hört sich so an, als würde es das Problem allein nicht lösen, BaseCSVWidget.value_from_datadict robuster zu machen, wie oben erwähnt? (Ich verstehe das Problem noch nicht ganz.)

Sie sollten keine in -Suche für m2m-Felder benötigen, da der Multiple-Choice-Filter bereits ein ähnliches Verhalten bietet.

in ermöglicht die Prüfung gegen mehrere Werte über exact . Wie überprüfe ich mehrere Werte, wenn ich ìn nicht verwenden kann?

PS: Können wir dieses Ticket wieder öffnen?

Es hört sich so an, als würde es das Problem allein nicht beheben, BaseCSVWidget.value_from_datadict robuster zu machen, wie oben erwähnt? (Ich verstehe das Problem noch nicht ganz.)

Möglicherweise. Wenn der Wert bereits eine Liste ist, sollte er einfach fortfahren und diese Liste zurückgeben. Allerdings halte ich es nicht für sinnvoll, das CSV-Verhalten mit dem SelectMultiple -Widget zu mischen.

in ermöglicht die Prüfung gegen mehrere Werte über exact . Wie überprüfe ich mehrere Werte, wenn ich ìn nicht verwenden kann?

Das ModelMultipleChoiceFilter konstruiert eine OR-Abfrage aus Q-Objekten. Wenn Sie also eine Abfragezeichenfolge wie /api/mymodel?m2m=a&m2m=b haben, erhalten Sie am Ende einen Filteraufruf wie

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

Mit der Version 2.1 wird lookup_expr jetzt auf jedes Q-Objekt angewendet, sodass Sie am Ende erhalten würden

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

Das obige ist ungültig und würde kaputt gehen, aber es ist sinnvoll für contains und andere Lookups, die mit Einzelwerten arbeiten sollen.

Kurz gesagt, alles, was Sie hier tun müssen, ist die exact -Suche für Ihr m2m-Feld zu verwenden.


Ich denke, das Todo hier ist:

  • Machen Sie BaseCSVWidget mit SelectMultiple $ kompatibel, indem Sie den Wert zurückgeben, wenn es sich bereits um eine Liste handelt. Oder wenn es nicht kompatibel sein sollte, sollten wir zumindest einen nützlichen Fehler auf init liefern.
  • Fix Meta.fields für m2m. Das Generieren einer in -Suche nach ModelMultipleChoiceFilter ist unsinnig. Wir sollten auch testen, was bei anderen Lookups wie isnull passiert, die einen booleschen Wert erwarten.

Ja, das sieht immer noch nicht nach einem Fehler aus, aber wie das alles funktioniert, ist unterdokumentiert, denke ich ...

Ich hatte gerade diesen Fehler und seine Erklärung ist in diesem Thread begraben, um es zusammenzufassen und zukünftigen Lesern eine lange Suche zu ersparen:

ManyToMany -Felder, die in filterset_fields der Viewsets deklariert sind, dürfen nicht in enthalten

Eine Warnung zur Laufzeit wäre sehr wünschenswert.

@marcosox Gute Idee. Gerne sehe ich mir eine PR an, die das hinzufügt!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen