2017-06-07 2 views
1

Ist es korrekt, dass die Filterung nach einer Liste von Werten in django_filter und durch das Django Rest Framework (DRF) zu kompliziert bleibt oder fehlt mir etwas Einfaches?django_filter Django Rest Framework (DRF) Handhabung query_params get vs getlist

Insbesondere müssen wir a-priori die Parameter angeben, die mit 'getlist' anstatt mit 'get', wenn wir einen Mehrwert Parameter via URL wie unten abgegeben verarbeiten behandelt werden sollte:

http://example.com?country=US&country=CN

wenn wir nun bearbeiten diese Anfrage,

request.query_params.get ('Land') sowie request.query_params [ 'Land'] fälschlicherweise zurückkehren 'KN' während r equest.query_params.getlist ('country') gibt ['US', 'CN'] zurück, was wir in diesem Beispiel wünschen.

Also meine aktuelle Logik diktiert, dass ich im Voraus angeben, welche Parameter haben können mehrere Werte angegeben, da ich von einem internen Verfahren nicht wissen, dass diese transparent behandelt:

So dies zu handhaben ich momentan tun so etwas wie dieses (alle die tatsächliche Anforderungsverarbeitung Skipping, Filtern usw. aus Gründen der Einfachheit):

class CountryFilter(filters.FilterSet): 
    """ placeholder: this is a separate question as to why I can't get any of these to work as desired. """ 
    #country = filters.CharFilter(method='filter_country') 
    #country = filters.CharFilter(name='country', lookup_expr='in') 
    #country = filters.MultipleChoiceFilter() 

class CountryViewSet(viewsets.ModelViewSet): 
    """Viewset to work with countries by ISO code""" 

    def list(self, request, *args, **kwargs): 
     """do something based on a request for one or more countries 
     such as http://example.com?country=CA&country=CN&country=BR 
     """ 
     query_params = request.query_params 
     filter0 = CountryFilter(query_params, queryset=Country.objects.all())  

     # if filter0 worked, we'd be done and wouldn't need below 
     # but it doesn't 

     query_dict = {} 
     params_to_list = ['country', 'another_param'] 
     for k in query_params.keys(): 
      if k in params_to_list: 
       query_dict[k] = query_params.getlist(k) 
      else: 
       query_dict[k] = query_params.get(k) 

     # use the newly counstructed query_dict rather than query_params 
     filter1 = CountryFilter(query_dict, queryset=Country.objects.all()) 

     import pdb; pdb.set_trace() 
     return HttpResponse('ok') 

theoretisch filter1 sollte geben Sie mir die gewünschten Ergebnisse, während filter0 nicht.

Code, der auf dieser basiert, funktioniert in meiner Produktionsumgebung, hat aber einige andere Effekte wie die gültige Liste wieder in eine Zeichenfolge umzuwandeln. Ich möchte hier nicht auf diese Frage eingehen, ich frage mich nur, ob die Leute das tun oder nicht?

Antwort

0

Insbesondere müssen wir a-priori die Parameter angeben, die mit 'getlist' anstatt mit 'get', wenn wir einen Mehrwert Parameter via URL wie unten abgegeben verarbeiten behandelt werden sollte:

Dies ist nicht erforderlich. Filter Instanzen erstellen ein Formularfeld, das wiederum die Abfrageparamvalidierung durchführt. Diese Felder sind Standard-Django-Formularfelder, und das Widget bestimmt, ob beim Extrahieren von Werten aus dem Abfrage-Dict .getlist() oder .get() verwendet werden soll. Sie müssen das Abfragedikt nicht vorverarbeiten.

In Ihrem Beispiel sollten sich filter0 und filter1 eigentlich identisch verhalten, da QueryDict('a=1&a=2') funktionell äquivalent zu dict(a=['1', '2']) für SelectMultiple Widgets ist. Sein .value_from_datadict() ruft .getlist() auf dem ehemaligen und .get() auf dem letztgenannten an - beide geben eine Liste der Werte zurück.


Um Ihr Problem der Filterung durch mehrere Werte zu lösen, haben Sie zwei Möglichkeiten.

  1. Verwenden einer MultipleChoiceFilter (docs)
  2. Verwenden einer CSV-basierten Filter (docs)

Multiple-Choice-Filter ein Auswahl multiple Widgets machen und erwartet einen Abfragezeichenfolgeflag wie ?a=1&a=b. Sie haben dies oben versucht, jedoch haben Sie choices ausgelassen. Ohne Auswahlmöglichkeiten wird die HTML-Auswahl ohne Optionen gerendert, und die Validierung schlägt anschließend fehl (da dieselben Optionen zur Validierung der Eingabe verwendet werden). Alternativ können Sie auch ModelMultipleChoiceFilter verwenden, was eine queryset erfordert, um die Auswahlmöglichkeiten zu erstellen. Beachten Sie, dass die Verarbeitung von queryset/choice in das Verhalten des zugrunde liegenden Formularfelds integriert ist und nicht für django-filter spezifisch ist.

Für die letztere Option gibt es mehr Details in den oben verlinkten Dokumenten, aber es ist erwähnenswert, dass es eine Texteingabe darstellt und eine Abfragezeichenfolge wie ?a=1,2 anstelle von ?a=1&a=2 erwartet.

Verwandte Themen