Hey All, This is somewhat related to the ticket “Process HTTP PUT into request.FILES and request.PUT as done for POST” [1], although much broader in scope.
In that ticket there’s a link to a related discussion [2] where Malcolm Tredinnick explains why a request.PUT that mirrors the current request.POST behaviour isn’t the right thing to do. Obviously Malcom’s right that request.POST is a special case that’s limited to supporting browser form submissions, which means restricting it’s behaviour to requests with a method “POST” [3] and a content type of either “application/x-www-form-urlencoded” or “multipart/form-data” (and apparently “text/plain” as per HTML5) [4]. However, it would still be useful if Django provided built-in behaviour to be able to support accessing the content of HTTP requests for requests other than just form POSTs, at some higher level than simply forcing the developer to use request.read() or request.raw_post_content. I don’t want to get too bogged down in implementation details at this point, more just gauge a response to the proposal - if it’s a “that sounds like a decent idea”, or if it’s a “ain’t never gonna happen in core” kind of thing, so I’ll (try!) to keep it reasonably high level for now... So... Generic Content-Type support could be provided either by re-purposing the request.REQUEST attribute (obviously making sure it’s done in a backwards compatible way [*]), or by adding a new lazily evaluated request.CONTENT. The request.REQUEST/request.CONTENT would be used to return the parsed content of the request, as determined by a set of installed parsers. A request.content_parsers attribute would also be added to the request [**], which would default to initializing from settings.CONTENT_PARSERS, and would used to determine which parser would be used to de-serialize the request. Django could initially ship with a FormParser (And perhaps also some other useful defaults such as a JSONParser.) The default for settings.CONTENT_PARSERS would probably just include the FormParser. The FormParser would handle “application/x-www-form-urlencoded” and “multipart/form-data requests”, and return a QueryDict containing both the content data and uploaded files, reusing the existing MultiPartParser in it's implementation. (and respecting the existing upload_handlers for file uploads.) You would probably also want to add a ‘content_parsers’ view decorator that could be used to set the list of parsers used on a given request at the view level. That’d mean that usage would look something like this... def standard_view(request, *arg, **kwargs): request.POST[‘foo’] # Does not work for form PUT requests request.REQUEST[‘foo’] # Now works for form PUT requests @content_parsers(JSONParser) # NB. Decorator would take either a tuple/list or a single value def json_view(request, *args, **kwargs): request.REQUEST[‘foo’] # Works for application/json requests @content_parsers(BinaryUploadParser) def put_upload_view(request, *args, **kwargs): uploaded_file = request.REQUEST # An UploadedFile object (*NB, I’m not suggesting here that Parsers would be limited to returning pre-parsed objects, you might also create streaming parsers that return generator objects.) I like the idea because it follows the same pattern as request.POST but supplements the behaviour to support arbitrary methods and content-types, which is pretty core stuff for anyone trying to do any Web API stuff with Django. Forcing the developer to drop down to request.raw_post_content/request.read() isn’t particularly helpful if we could provide a nice flexible out of the box solution. Obviously there’d be a lots to thrash out, and there’s also some fiddly complexity there (EG handling request.raw_post_content, see ticket 9054 [5]) but what do you peeps think of the idea in general? Is it something that’d be valuable in core, would it only merit further discussion if there was a more explicitly detailed proposal, or if there was a reference implementation, or is it entirely out-of-scope? All of what I’ve mentioned could equally be implemented as Middleware, so would that make more sense than implementing it in core, and if it was done as Middleware would it still be useful for inclusion into Django? I realise it’s a fairly big proposal (esp. for a first post to the list) so feel free to go ahead and shoot me right down! :) Cheers, Tom Possible interface ================== settings.CONTENT_PARSERS # List of parsers, defaults to (contentparsers.FormParser,) HttpRequest.parsers # property, defaults to initializing from settings.CONTENT_PARSERS HttpRequest.REQUEST *or* .CONTENT # lazily evalulated, like request.POST class ContentParser(object) # Interface for parser objects. handles_request(request) parse(request) class FormParser(ContentParser) # Multipart and urlencoded behaviour, probably returns QueryDict containing both form fields and files. class JsonParser(ContentParser) # Optional, return dict. class BinaryUploadParser(ContentParser) # Optional, returns UploadedFile # Possibly a GetParamsParser or BackwardsCompatParser if repurposing request.REQUEST. content_parsers # View decorator, sets .parsers on request BadRequestException # Thrown on malformed request content, 400 response UnsupportedMediaTypeException # Thrown if Content-Type on request is not supported by set of content_parsers, 415 response Notes ===== [*] EG The default for settings.CONTENT_PARSERS could be (BackwardsCompatParser,). Alternatively we could provide a GetParamsParser, and have the default for settings.CONTENT_PARSERS be (GetParamsParser, FormParser). That's slightly different in that the GetParamsParser wouldn't be parsing the request body like the other proposed parsers, but it might be quite nice in that request.REQUEST would continue to provide (almost) the existing behaviour, but in a more flexible, useful way. A possible migration might be to use CONTENT_PARSERS=(BackwardCompatParser,) as the default in 1.4 and move to CONTENT_PARSERS=(GetParamsParser, FormParser) in 1.5. [**] request.content_parsers would probably be implemented in the same pattern as request.upload_handlers [1] http://code.djangoproject.com/ticket/12635 [2] https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion [3] HTML4: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.1 HTML5: http://dev.w3.org/html5/spec/Overview.html#attr-fs-method [4] HTML4: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 HTML5: http://dev.w3.org/html5/spec/Overview.html#attr-fs-enctype [5] http://code.djangoproject.com/ticket/9054 -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.