Django-filter: [2.1.0] BaseCSVWidget.value_from_datadict — AttributeError : l'objet 'list' n'a pas d'attribut 'split'

Créé le 12 juil. 2019  ·  15Commentaires  ·  Source: carltongibson/django-filter

J'ai rencontré …

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'

et a constaté que le code à…
https://github.com/carltongibson/django-filter/blob/1f47e36b614724a8735e0457fa511dcaf5448481/django_filters/widgets.py#L195 -L202
… n'est pas robuste en ce qui concerne super().value_from_datadict(data, files, name) retournant une liste. J'ai besoin de déboguer davantage, mais peut-être savez-vous déjà ce qui se passe ici ?

Commentaire le plus utile

Je viens d'avoir cette erreur et son explication est enterrée dans ce fil, donc pour récapituler et économiser une longue recherche pour les futurs lecteurs :

Les champs ManyToMany déclarés dans les ensembles de vues filterset_fields ne doivent pas contenir in

Un avertissement lors de l'exécution serait très apprécié.

Tous les 15 commentaires

Salut @moseb. Pouvez-vous coller le code FilterSet que vous utilisez ? Les dernières lignes de la trace sont suffisantes pour déterminer pourquoi vous rencontrez cette exception.

Retraçage complet :

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

C'est en parcourant une liste où django-rest-framework génère le code.

Salut @moseb. Pouvez-vous coller le code FilterSet que vous utilisez ?

Pour être honnête, je n'en ai pas encore trouvé dans cette zone du code. Il pourrait être tout par DRF. Pas encore sûr.
Je suis conscient que mon rapport de bogue n'est pas idéal, mais c'est le meilleur que j'ai en ce moment. Pardon!

Votre vue API doit avoir soit filterset_class ou filterset_fields , ou si vous utilisez une ancienne version de django-filter, filter_class et filter_fields .

J'ai trouvé l'utilisation de filter_class - donc je suppose que vous venez de m'aider à trouver que la migration vers django-filter 2.0 n'a pas été correctement effectuée jusqu'à la fin. Cool!
Y a-t-il quelque chose qui empêche django-filter de détecter les restes pré-2.x et d'en avertir, conceptuellement? Est-ce qu'il avertit mais je l'ai manqué? Un avertissement de code à ce sujet serait-il un ajout bienvenu pour une demande d'extraction ?

La v2.0 a supprimé de nombreux avertissements de dépréciation existants, vous pouvez donc essayer d'exécuter votre suite de tests avec la v1.0 puis la v1.1 de django-filter.

En supposant que cela se résoudra lors de la mise à jour. Revenez sinon.

Il y a maintenant une démo reproductible du problème sur https://github.com/moseb/django-filter-issue-1103-demo. Merci d'envisager de rouvrir ce sujet. Merci!

Ah, cela est lié au fait ModelMultipleChoiceFilter n'est pas compatible avec le mixin CSV, ainsi qu'à la recherche in . Deux questions distinctes mais liées ici.

Vous ne devriez pas avoir besoin d'une recherche in pour les champs m2m, car le filtre à choix multiples fournit déjà un comportement similaire.

Salut!

Ah, cela est lié au fait ModelMultipleChoiceFilter n'est pas compatible avec le mixin CSV, ainsi qu'à la recherche in . Deux questions distinctes mais liées ici.

Existe-t-il des tickets sur GitHub pour ces problèmes ?

Il semble que rendre BaseCSVWidget.value_from_datadict plus robuste, comme mentionné ci-dessus, ne résoudra pas le problème ? (Je ne comprends pas encore tout à fait le problème.)

Vous ne devriez pas avoir besoin d'une recherche in pour les champs m2m, car le filtre à choix multiples fournit déjà un comportement similaire.

in permet de vérifier plusieurs valeurs sur exact . Si je ne peux pas utiliser ìn , comment puis-je vérifier plusieurs valeurs ?

PS : Peut-on rouvrir ce ticket ?

Il semble que rendre BaseCSVWidget.value_from_datadict plus robuste, comme mentionné ci-dessus, ne résoudra pas le problème ? (Je ne comprends pas encore tout à fait le problème.)

Peut-être. Si la valeur est déjà une liste, il suffit de continuer et de renvoyer cette liste. Cela dit, je ne pense pas qu'il soit logique de mélanger le comportement CSV avec le widget SelectMultiple .

in permet de vérifier plusieurs valeurs sur exact . Si je ne peux pas utiliser ìn , comment puis-je vérifier plusieurs valeurs ?

Le ModelMultipleChoiceFilter construit une requête OU à partir d'objets Q. Donc, si vous avez une chaîne de requête comme /api/mymodel?m2m=a&m2m=b , vous vous retrouverez avec un appel de filtre comme

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

Avec la version 2.1, le lookup_expr est maintenant appliqué à chaque objet Q, donc vous vous retrouveriez avec

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

Ce qui précède n'est pas valide et ne fonctionnerait pas, mais cela a du sens pour contains et d'autres recherches destinées à fonctionner avec des valeurs uniques.

Donc, en bref, tout ce que vous avez à faire ici est d'utiliser la recherche exact pour votre champ m2m.


Je pense que le todo ici est:

  • Rendez BaseCSVWidget compatible avec SelectMultiple en retournant la valeur si c'est déjà une liste. Ou s'il ne devrait pas être compatible, nous devrions au moins fournir une erreur utile sur init.
  • Fixez Meta.fields pour m2m. Générer une recherche in pour ModelMultipleChoiceFilter est absurde. Nous devrions également tester ce qui se passe pour d'autres recherches comme isnull , qui attend un booléen.

Oui, cela ne ressemble toujours pas à un bogue , mais la façon dont tout cela fonctionne est sous-documentée, je suppose ...

Je viens d'avoir cette erreur et son explication est enterrée dans ce fil, donc pour récapituler et économiser une longue recherche pour les futurs lecteurs :

Les champs ManyToMany déclarés dans les ensembles de vues filterset_fields ne doivent pas contenir in

Un avertissement lors de l'exécution serait très apprécié.

@marcosox Bonne idée. Heureux de regarder un PR ajoutant cela !

Cette page vous a été utile?
0 / 5 - 0 notes