2

I'm trying to serialize an array that comes from a form. The array looks like this:

some_id: 12
array1[]:1
array1[]:2
array2[]:5
array2[]:6

or, as it arrives in request.POST

<QueryDict: {
    'array1[]': ['1', '2'], 
    'array2[]': ['5', '6'], ...

Now I get the serializer working for the simple id: some_id = serializers.IntegerField(), which is great. I've tested it without the arrays and everything works as expected.

For the array I thought I found this serializer field: listfield
I tried the following implementation:

array1 = serializers.ListField(
    child=serializers.IntegerField(min_value=1)
)

This looks like the manual says how it works, but it only seems to give me the following error:

TypeError at /api/myendpoint/ 'int' object is not iterable

I'm not really sure where I went wrong, maybe I'm on the wrong track (really not a Django guy :) ), but I'm sure it's a small mistake.

So how can I serialize a posted array (or should I call it a Dict?)

The complete trace doesn't seem to be containing anything strange, but i've kept it available on pastebin

The data is posted with this jquery code:

var mydata = {
    array1: [1,2],
    array2: [5,6],
    some_id: 12
};

$.ajax({
    url: "http://domain.com//api/myendpoint/",
    dataType: "json",
    data: mydata,
    method: "POST"
})

I'm using:

  • Django 1.10.2
  • djangorestframework 3.5.2
  • jquery 2.1.4
Nanne
  • 64,065
  • 16
  • 119
  • 163
  • Can you include the full traceback? [I just tried it](http://pastebin.com/BquZZvmj) and granted it didn't get the right values from my test inputs but I didn't get the error either – Sayse Jan 23 '17 at 21:17
  • @Sayse yeah sure, I'll add it to the post ! – Nanne Jan 23 '17 at 21:24
  • @Sayse `>>>data = {'my_arr[]' : ['1', '2']}` ? – Gocht Jan 23 '17 at 21:37
  • @Gocht Is that a strange/dubious format? I have a simple object created in jquery (see the first 'array'), which I post. I checked the `request.POST` to see how it looked in django, but don't (or shouldn't) do anything interesting to it. – Nanne Jan 23 '17 at 21:45
  • 1
    @Sayse you got `None` from `print(val.validated_data.get('my_arr'))` because you used `'my_arr[]'` instead of `'my_arr'` as key. See http://pastebin.com/8Gsd263Y – Gocht Jan 23 '17 at 21:55
  • @Nanne you need to ensure your JS code is creating the request body with the right key names. `'array1'` is not the same with `'array1[]'` – Gocht Jan 23 '17 at 21:56
  • @Gocht - Interesting, I had tried both but both returned empty querysets, using the `child` keyword arg for the listfield appears to have altered this – Sayse Jan 23 '17 at 22:00
  • @Gocht ow, woah, interesting, sounds like an issue indeed. not sure how to fix that sadly. I'm just adding them to an object and then posting them. The json-key is 'array1', and when posting it it gets the `array[]` automatically. I'm just making it something like `var test = {'test':[1,2]};`, and then posting it trough jquery. Do you think I messed that up? I can add that piece of js if you think that helps? – Nanne Jan 23 '17 at 22:06
  • You can post your JS code and tag this question as JS/JQuery also. – Gocht Jan 23 '17 at 22:09
  • I'll fix both tomorrow, have to run sadly. Thanks for the start though! – Nanne Jan 23 '17 at 22:19
  • You should also mention that this is using the Django Rest Framework library. – Daniel van Flymen Jan 23 '17 at 22:51
  • Good point @DanielvanFlymen , fixed that. Now i'm just trying to figure out if there's an easy front-end fix to get this rolling. – Nanne Jan 24 '17 at 06:49
  • I wonder if this would help, if the `[]` is the issue! http://stackoverflow.com/questions/5497093/what-is-traditional-style-of-param-serialization-in-jquery – Nanne Jan 24 '17 at 08:16
  • You may also want to try `JSON.stringify(mydata)` – Sayse Jan 24 '17 at 09:04
  • @Sayse so i'd send my data as one json string. Would the serializer accept that automatically as valid input (so find the separate variables etc), or do I have to fix that on the backend as well? – Nanne Jan 24 '17 at 10:41
  • @Nanne - I think its something DRF can handle for you (I've written things like it in that many ways though, including with `$.param`, I can't remember which way is correct right now) – Sayse Jan 24 '17 at 10:45

2 Answers2

2

Long story short: I've added this to my javascript:

jQuery.ajaxSettings.traditional = true;

and now it works like you'd expect

short story long:

The biggest issue was that a 'simple' post isn't as simple as I thought. From the question, this was what jQuery was posting:

some_id: 12
array1[]:1
array1[]:2
array2[]:5
array2[]:6

The object that was send from the frontend, and was expected in the backend contained properties called array1 and array2, but what was send was literally array1[] and array2[]. While the solution by Daniel van Flymen will probably work to use these properties, I have been looking at fixing these brackets.

From this rather old jquery topic I found that jQuery adds these for languages like php and ruby, but as I found out: python doesn't like it

With the addition of jQuery.ajaxSettings.traditional = true;, the headers now look like this:

some_id: 12
array1:1
array1:2
array2:5
array2:6

and this is what the serializer understands as expected.

Nanne
  • 64,065
  • 16
  • 119
  • 163
1

Your values aren't being sent as an array. They are being sent as individual keys.

If your intention is to deserialize the keys to an array, then you can override the to_internal_value() method on your serializer:

class CustomSerializer(serializers.Serializer):
    my_array = serializers.ListField(
        child=serializers.IntegerField(min_value=1)
    )

    def to_internal_value(self, data):
        my_array = []
        for key, value in data.items():
            if "array[]" in key:
                my_array.append(value)

        # Perform some validation on your array
        for item in array:
            if item > 10:
                raise ValidationError(
                    {
                        'my_array': "A number was higher than 10."
                    }
                )

        # Return the validated values    
        return {
            'my_array': my_array
        }

The to_internal_value() method is responsible for deserializing data. Hopefully this points you in the right direction. Be sure to check out the example in the documentation on to_internal_value().

Daniel van Flymen
  • 10,931
  • 4
  • 24
  • 39
  • This looks good for fixing my current problem, thanks! The core-issue seems to be that i'm a strange post from the front-end, so I'm hoping a solution can be found that fixes that, because that seems closer. I'll see if that can be fixed. – Nanne Jan 24 '17 at 06:48
  • No sweat @Nanne, be sure to mark as correct if it helped. – Daniel van Flymen Jan 24 '17 at 15:24