django-rest-framework icon indicating copy to clipboard operation
django-rest-framework copied to clipboard

Clearing a ManyToManyField in a multipart PATCH Request

Open DavidMuller opened this issue 9 years ago • 6 comments

Hello,

I'm having some trouble clearing a ManyToMany field in a multipart PATCH request.

In a JSON request, I would clear my ManyToMany field, 'many_to_many_field' by PATCHing something like:

{"many_to_many_field": []}

However, in a multipart PATCH request, sending values including '[]', '', 'null', 'None', etc all yield the following error:

["Incorrect type. Expected pk value, received unicode."]

It seems that ManyRelatedField.get_value() does not recognize any of those options as special indicators of empty.

DavidMuller avatar Apr 27 '15 23:04 DavidMuller

I think the crux of this boils down to the fact that there's no way to encode [] in multipart?

from rest_framework.test import APIClient
client = APIClient()
client._encode_data({'foo':[1, 2, 3]}, format='multipart')

('--BoUnDaRyStRiNg\r\nContent-Disposition: form-data; name="foo"\r\n\r\n1\r\n--BoUnDaRyStRiNg\r\nContent-Disposition: form-data; name="foo"\r\n\r\n2\r\n--BoUnDaRyStRiNg--\r\n', u'multipart/form-data; boundary=BoUnDaRyStRiNg; charset=utf-8')

client._encode_data({'foo': []}, format='multipart')
('--BoUnDaRyStRiNg--\r\n', u'multipart/form-data; boundary=BoUnDaRyStRiNg; charset=utf-8')

DavidMuller avatar Apr 28 '15 01:04 DavidMuller

I think the crux of this boils down to the fact that there's no way to encode [] in multipart?

Correct. This is a constraint of HTML form input. We can either document this, or instead simply not display (or disable with a tooltip) the PATCH button for form input, and only enable it for raw JSON input.

See also #2894

tomchristie avatar May 01 '15 09:05 tomchristie

My workaround is listed below. Hope we will have a nice solution later.

Using null when firing an ajax request.

if (subscribersList.select2('data').length === 0) {
    data.push({"name": "subscribers", "value": null})
}

Add some code to clear ManyToManyField in is_valid method

def is_valid(self, raise_exception=False):
    data = self.get_initial()
    if data.get('subscribers')[0] == '':
        self.instance.subscribers.clear()
        del self.fields['subscribers']
    return super().is_valid(raise_exception)

guhuajun avatar Nov 21 '17 03:11 guhuajun

This comment from 2017 just saved my sanity. Would it be something to consider, making this a supported solution? I.e. We allow "None" with allow_null=True, so why not allow "[]" or "None" in a ManyToMany html submit?

halfnibble avatar Aug 14 '23 03:08 halfnibble

I am open to review an implementation which can implement it properly without any side effect

auvipy avatar Aug 15 '23 09:08 auvipy

This comment from 2017 just saved my sanity. Would it be something to consider, making this a supported solution? I.e. We allow "None" with allow_null=True, so why not allow "[]" or "None" in a ManyToMany html submit?

I have totally no idea for what I was doing at that moment. :) It's glad to see the snippet can help you a little bit. Nowadays, I am just using Starlette + Vue (and Naive UI) if I need to create some demos.

guhuajun avatar Sep 12 '23 00:09 guhuajun