Serve images with django-rest-framework - django

I am trying to serve images from an api based on django-rest-api.
So far I have a very basic api view that loads an image and its mimetype and returns it.
#api_view(['GET'])
def get_pic(request, pk=None, format=None):
//get image and mimetype here
return HttpResponse(image, content_type=mimetype)
This has been working fine, however I have tested it on some new image libraries on iOS and have found that the API is returning a 406 error.
I believe this is because the client is sending a Accept image/* which this api view doesn't except.
Doing a HEAD on the api returns the following;
GET, OPTIONS
Connection → keep-alive
Content-Type → application/json
Date → Wed, 17 Jun 2015 10:11:07 GMT
Server → gunicorn/19.2.1
Transfer-Encoding → chunked
Vary → Accept, Cookie
Via → 1.1 vegur
X-Frame-Options → SAMEORIGIN
How can I change the Content-Type for this API to accept image requests?
I Have tried adding a custom parser with the right media_type but his doesn't seem to help. Is this the right approach or is there an easier way to serve images from django-rest-framework?

You need to create a custom renderer like:
class JPEGRenderer(renderers.BaseRenderer):
media_type = 'image/jpeg'
format = 'jpg'
charset = None
render_style = 'binary'
def render(self, data, media_type=None, renderer_context=None):
return data

Related

DRF additional action without suffix [duplicate]

I'm working with two dev servers on my local machine (node & django's).
I've added django-cors-headers to the project to allow all origins & methods (on dev) with the following settings :
CORS_ORIGIN_ALLOW_ALL = 'ALL'
CORS_ALLOW_METHODS = (
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
)
I'm getting 405 when attempting DELETE.
Looking at the response headers
HTTP/1.0 405 METHOD NOT ALLOWED
Date: Mon, 03 Nov 2014 10:04:43 GMT
Server: WSGIServer/0.1 Python/2.7.5
Vary: Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Access-Control-Allow-Origin: *
Allow: GET, POST, HEAD, OPTIONS
Notice that DELETE & PATCH / PUT are not present in the allowed methods list.
Is there something missing from my configuration ?
The response looks very similar to that of the list view (/api/resource/) for a ViewSet. List views only support GET, to list all of the objects, and POST to create a new object.
DELETE requests are only allowed on the detail view (/api/resource/1/). This is because Django REST Framework needs to know what object you are looking to delete, and this information cannot be retrieved from just the list view.
If you need to connect http method DELETE with URL without pk in DRF try this inside of your ModelViewSet:
#action(methods=['delete'], detail=False)
def delete(self, request):
# your code
UPD: Note that action attribute inside of ModelViewSet class will be None due request. If you check it somewhere, handle not only action name, but request method and request path.

Missing form data in request

I have following code
class MyClass(restful.Resource):
def get(self):
headers = {'Content-Type': 'text/html'}
return make_response(render_template('myfile.html'),200,headers)
def post(self):
session['CONSUMER_KEY']=request.form.get('consumer_key')
session['CONSUMER_SECRET']=request.form.get('consumer_secret')
render_template('myfile.html')
api.add_resource(MyClass,"/mag/",endpoint="mag")
I have written following test:
def mytest(self):
content_type={"Content-Type": "application / x - www - form - urlencoded","Content-Disposition": "form-data"}
response = self.client.post(
api.url_for(MyClass), data = json.dumps({'consumer_key':'testconsumerkey',
'consumer_secret':'testconsumersecret'}),
headers=content_type
)
The issue is form data is blank and thats the values are not getting set in session. When i debug i see that request.data is populated but request.form is an empty dictionary. Can someone suggest how I can send form data in a post request from a test
EDIT: Environment details
Python 2.7, Flask web framework, self.client is . I am using flask.ext.testing
You seem to be confused as to what the expected format for the post body should be. Should it be JSON data (which is what you send in the test case), or should it be in the application/x-www-form-urlencoded format (which is what you claim to send in the test case, and what the endpoint will read)?
If you wish to receive JSON data, you'll need to change the endpoint to read the data from request.get_json(). You'll also need to use application/json as the Content-Type header in the test case.
If you wish to receive urlencoded post data, then just simplify the test case by removing the Content-Type header and the json.dumps. Just pass the data dict to the data argument.

Django/DRF - 405 Method not allowed on DELETE operation

I'm working with two dev servers on my local machine (node & django's).
I've added django-cors-headers to the project to allow all origins & methods (on dev) with the following settings :
CORS_ORIGIN_ALLOW_ALL = 'ALL'
CORS_ALLOW_METHODS = (
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
)
I'm getting 405 when attempting DELETE.
Looking at the response headers
HTTP/1.0 405 METHOD NOT ALLOWED
Date: Mon, 03 Nov 2014 10:04:43 GMT
Server: WSGIServer/0.1 Python/2.7.5
Vary: Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Access-Control-Allow-Origin: *
Allow: GET, POST, HEAD, OPTIONS
Notice that DELETE & PATCH / PUT are not present in the allowed methods list.
Is there something missing from my configuration ?
The response looks very similar to that of the list view (/api/resource/) for a ViewSet. List views only support GET, to list all of the objects, and POST to create a new object.
DELETE requests are only allowed on the detail view (/api/resource/1/). This is because Django REST Framework needs to know what object you are looking to delete, and this information cannot be retrieved from just the list view.
If you need to connect http method DELETE with URL without pk in DRF try this inside of your ModelViewSet:
#action(methods=['delete'], detail=False)
def delete(self, request):
# your code
UPD: Note that action attribute inside of ModelViewSet class will be None due request. If you check it somewhere, handle not only action name, but request method and request path.

Any idea why I can't POST to this Django REST API?

I'm currently trying to get a POST request using multipart/form-data running to the Django REST framework. I've successfully run through some test requests via the interactive API screens, which work fine. I've then tried to convert these over to using a non-Session based auth strategy, and I've consistently got errors. The requests I've sent are of the form:
POST /api/logs/ HTTP/1.1
Host: host:8080
Connection: keep-alive
Content-Length: 258
Accept: application/json
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTOhRsMbL8ak9EMQB
Authorization: Token -token-
------WebKitFormBoundaryx6ThtBDZxZNUCkKl
Content-Disposition: form-data; name="SubmittedAt"
2014-01-23T10:39:00
------WebKitFormBoundaryx6ThtBDZxZNUCkKl
Content-Disposition: form-data; name="Device"
CheeseDevice
------WebKitFormBoundaryx6ThtBDZxZNUCkKl--
Sadly, the result has been (for all the requests I've run):
{"Device": ["This field is required."], "SubmittedAt": ["This field is required."], "LogFile": ["This field is required."]}
Interestingly, I've been able to send chunks of JSON through to the endpoint, and they're accepted as expected, eg:
POST /api/logs/ HTTP/1.1
Content-Type: application/json
Host: host:8080
Connection: keep-alive
Content-Length: 35
Accept: application/json
Authorization: Token -token-
{
"Device": "CheeseDevice"
}
Returns:
{"SubmittedAt": ["This field is required."], "LogFile": ["This field is required."]}
As expected - it actually accepts the Device argument and only raises errors on the missing items. I'd switch to using JSON, but sadly cannot upload files with it...
Thanks in advance for any help!
Edit:
Further investigation (ie: writing a view method that returns the request data shows that request.DATA isn't getting populated, for some reason. Method I'm using to debug follows:
def test_create(self, request, pk=None):
return Response(request.DATA)
Edit 2:
Even further investigation (and dropping code chunks into the framework for debugging) indicates that the requests are getting caught up in _perform_form_overloading and never hitting the MultiPartParser. Not sure why this is occurring but I'll try and trace it further.
After delving down every level I could find...
Looks like the problem stems from the line endings - ie: the libs and request senders I've been using send the content through with "\n" (LF) endings, while the HTTP spec requires "\r\n" endings (CR,LF)
This hinges on the following code in the Django core, within http/multipartparser.py - in parse_boundary_stream:
header_end = chunk.find(b'\r\n\r\n')
For dev purposes (and because it's going to be way easier to patch at the Django end than in the clients...) I've switched the above line to:
header_end = chunk.replace("\r\n","\n").find(b'\n\n')
This updated code follows the recommendations in Section 19.3 of the HTTP/1.1 spec regarding Tolerant Applications and accepting LF instead of just CRLF - I'll try and get around to seeing if this is suitable for inclusion in the Django core.
Edit:
For reference, the patch is up on GitHub: https://github.com/tr00st/django/commit/9cf6075c113dd27e3743626ab0e18c6616488bd9
This could be due to malformed multipart post data.
Also possible that you don't have MultiPartParser installed, but I don't think that'll be it as you'd normally expect to see a 415 Unsupported Media Type response in that case.

Django: row still displayed after being deleted - because of caching?

I've written a Django app that uses DataTables. The problem is when I delete a row from the table it's still displayed in the table when running against nginx/gunicorn. However, it works correctly when I'm running against the Django test server. So if I start a server with this command line:
python manage.py runserver 192.168.0.1:8000
everything works fine. That is, I delete the row, the table refreshes, and the deleted row is not displayed.
This is a summary of the HTTP calls:
// An initial GET command to populate the table
GET /myapp/get_list (returns a list to display)
// I now select a row and delete it which causes this POST to fire
POST /myapp/delete (deletes a row from the list)
// After the POST the code automatically follows up with a GET to refresh the table
GET /myapp/get_list (returns a list to display)
The problem is when I use nginx/gunicorn the second GET call returns the same list as the first GET including the row that I know has been deleted from the backend database.
I'm not sure it's a caching problem either because this is the response header I get from the first GET:
Date Fri, 23 Dec 2011 15:04:16 GMT
Last-Modified Fri, 23 Dec 2011 15:04:16 GMT
Server nginx/0.7.65
Vary Cookie
Content-Type application/javascript
Cache-Control max-age=0
Expires Fri, 23 Dec 2011 15:04:16 GMT
The problem can be solved also by sending an added parameter to the server so that the browser doesn't cache the call. With jQuery you can simply use:
$.ajaxSetup({ cache: false});
Otherwise you must creat manually the parameter. Usually you create a timestamp
var nocache = new Date().getTime();
//send nocache as a parameter so that the browser thinks it's a new call
You can use Django's add_never_cache_headers or never_cache decorator to tell the browser not to cache given request. Documentation is here. I thinks it's cleaner solution than forcing the cache off in the javascript.
from django.utils.cache import add_never_cache_headers
def your_view(request):
(...)
add_never_cache_headers(response)
return response
from django.views.decorators.cache import never_cache
#never_cache
def your_other_view(request):
(...)
try this
oTable.fnDeleteRow( anSelected, null, true );
and b.t.w what version of datatables do you use?
I fixed the problem by changing
GET /myapp/get_list
to
POST /myapp/get_list
After doing this, I no longer get a cached response.