How to parse multipart/form-data from a put request in django
I want to submit a form to my backend and use the form data as the initial value for my form. Simple stuff if you are using a POST request:
def intervals(request, **kwargs):
form = MyForm(initial=request.POST)
However, I am sending a form that should replace the current resource, which should idiomatically be a PUT
request (I am using HTMX which allows you to do that). The problem is that I cannot find out how I can parse the form data from a put request. request.PUT
does not exist and QueryDict
only works for query params. What am I missing here?
The request.POST
is constructed by a method:
def _load_post_and_files(self):
"""Populate self._post and self._files if the content-type is a form type"""
if self.method != "POST":
self._post, self._files = (
QueryDict(encoding=self._encoding),
MultiValueDict(),
)
return
if self._read_started and not hasattr(self, "_body"):
self._mark_post_parse_error()
return
if self.content_type == "multipart/form-data":
if hasattr(self, "_body"):
# Use already read data
data = BytesIO(self._body)
else:
data = self
try:
self._post, self._files = self.parse_file_upload(self.META, data)
except (MultiPartParserError, TooManyFilesSent):
# An error occurred while parsing POST data. Since when
# formatting the error the request handler might access
# self.POST, set self._post and self._file to prevent
# attempts to parse POST data again.
self._mark_post_parse_error()
raise
elif self.content_type == "application/x-www-form-urlencoded":
# According to RFC 1866, the "application/x-www-form-urlencoded"
# content type does not have a charset and should be always treated
# as UTF-8.
if self._encoding is not None and self._encoding.lower() != "utf-8":
raise BadRequest(
"HTTP requests with the 'application/x-www-form-urlencoded' "
"content type must be UTF-8 encoded."
)
self._post = QueryDict(self.body, encoding="utf-8")
self._files = MultiValueDict()
else:
self._post, self._files = (
QueryDict(encoding=self._encoding),
MultiValueDict(),
)
so essentially replace this, but then without the guard for self.method
:
def load_post_and_files(request):
if request._read_started and not hasattr(request, "_body"):
request._mark_post_parse_error()
return
if request.content_type == "multipart/form-data":
if hasattr(request, "_body"):
# Use already read data
data = BytesIO(request._body)
else:
data = request
try:
return request.parse_file_upload(request.META, data)
except (MultiPartParserError, TooManyFilesSent):
# An error occurred while parsing POST data. Since when
# formatting the error the request handler might access
# request.POST, set request._post and request._file to prevent
# attempts to parse POST data again.
request._mark_post_parse_error()
raise
elif request.content_type == "application/x-www-form-urlencoded":
# According to RFC 1866, the "application/x-www-form-urlencoded"
# content type does not have a charset and should be always treated
# as UTF-8.
if request._encoding is not None and request._encoding.lower() != "utf-8":
raise BadRequest(
"HTTP requests with the 'application/x-www-form-urlencoded' "
"content type must be UTF-8 encoded."
)
return QueryDict(request.body, encoding="utf-8"), MultiValueDict()
else:
return = (
QueryDict(encoding=request._encoding),
MultiValueDict(),
)
and thus work with:
def intervals(request, **kwargs):
data, files = load_post_and_files(request)
form = MyForm(initial=request.POST)
You can also use the Django REST framework, which does the parsing for all HTTP methods by essentially overriding the ._load_post_and_files()
method, and then call a method named ._parse(..)
.