1

Tried to use DRF's ListField option to de-serialize list of values (applications in the example below) in query params. I'm having trouble making it work. Couldn't find with examples in the web. Hoping someone to throw some help.

api: /getAppStats/?applications=one,two,three

class MySerializer(serializers.Serializer):
 applications = serializers.ListField(child=serializers.CharField())
 start_date = serializers.DateField(default=(datetime.datetime.utcnow() - datetime.timedelta(days=30)).date().isoformat())
 end_date = serializers.DateField(default=datetime.datetime.utcnow().date().isoformat())

class SomeView(generics.GenericAPIView):
 """

 """
 permission_classes = [AllowAny]
 serializer_class = MySerializer

 def get(self, request, *args, **kwargs):

    """ 
    Just return query params..
    """
    serializer = MySerializer(data=request.query_params)

    if not serializer.is_valid():
        return Response({'stats':'invalid input data'})

    return Response({'stats':serializer.data})

All I see is this -

 {
            "stats": {
                "applications": [],
                "start_date": "2015-05-27",
                "end_date": "2015-06-26"
            }
        }

Am I sending the input params in incorrect way? Did I miss something trivial?

Thanks!

Satish
  • 13
  • 1
  • 4

3 Answers3

3

The standard approach to send multiple parameters for same key is is to use the same key name twice.

You can do this:

/getAppStats/?applications=one&applications=two&applications=three

Also, your server will receive the applications as an array i.e. as applications[] and not applications.

class SomeView(generics.GenericAPIView):
 """

 """
 permission_classes = [AllowAny]
 serializer_class = MySerializer

 def get(self, request, *args, **kwargs):

    """ 
    Just return query params..
    """

    # get the applications list
    applications = request.query_params.getlist('applications[]')

    # create a dictionary and pass it to serializer
    my_data = {'applications': applications, ...}

    serializer = MySerializer(data=my_data)

    if not serializer.is_valid():
        return Response({'stats':'invalid input data'})

    return Response({'stats':serializer.data})
Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • Thanks! This works but I wanted to avoid code to pull these values from query params manually and pass it again to serializer. I was expecting the serializer do that for me. – Satish Jul 01 '15 at 21:28
  • Also,wouldn't the url look long and redundant if I want to get information about more number of applications at once (say 20 or more)? – Satish Jul 01 '15 at 21:31
  • Serializer would have been able to pull these values from query params had the serializer field been `applications[]`. Since the data we are sending to serializer has `applications[]` key but serializer has `applications` field defined in it, it won't be able to get the initial data for `applications` key. – Rahul Gupta Jul 02 '15 at 05:52
  • Yes the url would become long if you use the general approach but it is still valid and permissible if the length of url is less than 2k characters. Check this link for the url length limit. http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers – Rahul Gupta Jul 02 '15 at 06:03
1

This has been answered but I was also looking for a solution that did not involve reforming the serializer data by getting the values out of the request with getlist (otherwise what's the point).

If you use the ListField (and probably also if you use many=True) there is code in there that will handle the list, the problem is that it looks like you are using jQuery on the client which seems to cause the "ids[]" syntax which fouls up the serializers.

Here was the solution I would up using.

Community
  • 1
  • 1
Ray Pendergraph
  • 449
  • 5
  • 19
  • Isn't `ids[]` format standard for query params. Browsers submit HTTP form posts in this format by default. I'm surprised DRF doesn't handle this. – andho Jul 31 '17 at 13:45
0

But I wanted to avoid code to pull these values from query params manually and pass it again to serializer. I was expecting the serializer do that for me. – Satish

I also want the serializer to do this for me, but I haven't found it. And besides, I use this list of query_params for django-filter: MultipleChoiceFilter and ModelMultipleChoiceFilter, so DRF ListField will not work for me.

In my project, the Android request without [], but only IOS request with [].

My solution is to add a decorator to add data in request.query_params and request.data.

def update_get_list_params(func):
    def wraps(self, request, *args, **kwargs):
        request.query_params._mutable = True
        request.data._mutable = True
        for key in list(request.query_params.keys()):
            # Make sure you know this will not influence the other query_params
            if key.endswith('[]'):
                new_key = key.split('[]')[0]
                value = request.query_params.getlist(key)
                if value:
                    request.query_params.setlist(new_key, value)
        for key in list(request.data.keys()):
            if key.endswith('[]'):
                new_key = key.split('[]')[0]
                value = request.data.getlist(key)
                if value:
                    request.data.setlist(new_key, value)
        return func(self, request, *args, **kwargs)
    return wraps


@update_get_list_params
def get(self, request, *args, **kwargs):
     pass
Cloud
  • 2,859
  • 2
  • 20
  • 23