I have some existing python code that uses django-piston which returns a dictionary as its response. For example:
from piston.handler import BaseHandler
class FooHandler(BaseHandler):
allowed_methods = ('GET',)
#classmethod
def create(self, request):
return { 'foo': 'bar' }
This code works fine, and is serialized into JSON with the appropriate HTTP header set (I'm assuming this works by some piston magic involving emitters; for bonus points, feel free to clarify how this behavior works as well, as I'm still getting to know django-piston).
I need to be able to modify the response in arbitrary ways, e.g. setting headers, status codes, etc. without using some pre-baked solution designed for a specific purpose. Is there a convenient way to access the response object in the context of this code and manipulate it, or has the response object not yet been created? In order to get access to a response object, will I have to construct it manually (a la vanilla django), serialize the dictionary, and set the appropriate headers by hand, and thus lose out on some of the useful magic of django-piston?
Every django-piston method returns an HTTPResponse.
When you return that dictionary, django-piston is just serializing it and sticking it in an HTTPResponse that it has crafted of some variety.
Kind of surprised you didn't pick up on that given that those "return rc.CREATED" lines in all the django-piston examples in the wiki are just hyper-simplistic responses with an HTTP code and response body.
Take a look here: https://bitbucket.org/jespern/django-piston/src/c4b2d21db51a/piston/utils.py
at the rc_factory class, which creates a variety of simple HTTPResponse objects for use with Piston.
At the very least you can observe how they do it, and then craft your own.
But the answer to the essence of your question "can I make a custom HTTPResponse" is yes, you can. Of course, that's what web servers do.
It is possible to set a custom response code by returning an HttpResponse object from your handler methods.
return HttpResponse({'foo': 'bar'}, status=404)
Unfortunately, you cannot set headers in the same way. For this, you have to write a custom Emitter that sets the headers you need. Something along these lines might work:
class CustomEmitter(JSONEmitter):
def render(self, request):
content = super(CustomEmitter, self).render(request)
response = HttpResponse(content)
response['Cache-Control'] = 'max-age=60'
Emitter.register('json', CustomEmitter, 'application/json; charset=utf-8')
You are quite right that django-piston uses emitters to serialize/deserialize requests and responses. You can even register your own emitters with piston and have it use those.
There are several ways that you can determine what the format should be.
1. mime-type
2. <format> in your URL
3. .json at the end of your URL
Which particular headers are you wanting to manipulate? There may be other solutions then just hacking away at the response object.
Related
in function view:
def view(request):
# do something with request
response = render(request, 'view.html')
# do something with response
return response
but now I have a View class:
class ArticleDeleteView(View):
pass
# or even something more complicated
class ArticleDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
pass
which function shall I overwrite to access to the response?
sorry that it may seems a silly question to you, but I looked through the docs and failed to find an answer.
thanks for all suggestions in comment. here's why I need to access to the response. Conditional Django Middleware (or how to exclude the Admin System)
I am building a specific type of 'middleware', for certain class based view, they are not actually middleware since they are not called for each request. I need to access to the response in order to do something before and after the response then return it, therefore I much would like to know which function generate response.
The easiest answer is: you don't. The View classes tend to be uncomplicated enough to be easily rewritten as a custom view which inherently gives you access to the response.
However if you insist I guess one of the functions you could override is the following: as_view, dispatch or setup as the request function goes through all of them. Sadly I couldn't find any mention of the one that was intended for that purpose.
I'm using a django framework for my assignment. I need to view what an http request object contains. print(request) is obviously not working. If I can see the object like in a json structure it would've been a huge help to understand what it would look like and what are the values it contains.
The best way to see what request contains (and more to that - any other object in python) is to use simple:
print(request.__dict__)
Depending on what type of request is GET or POST. You can find print(request.GET) or in print(request.POST) which is of <type 'dict'> and you can access data simply by request.GET.get("somedata") will return None if not found
Note: If you want to see attributes of request object you can do print(dir(request)) and you can do that on any object.
For more quick help you can do help(request) in python shell.
Apologies if my question is very similar to this one and my approach to trying to solve the issue is 100% based on the answers to that question but I think this is slightly more involved and may target a part of Django that I do not fully understand.
I have a CMS system written in Django 1.5 with a few APIs accessible by two desktop applications which cannot make use of cookies as a browser does.
I noticed that every time an API call is made by one of the applications (once every 3 seconds), a new entry is added to django_session table. Looking closely at this table and the code, I can see that all entries to a specific URL are given the same session_data value but a different session_key. This is probably because Django determines that when one of these calls is made from a cookie-less application, the request.session._session_key is None.
The result of this is that thousands of entries are created every day in django_session table and simply running ./manage clearsessions using a daily cron will not remove them from this table, making whole database quite large for no obvious benefit. Note that I even tried set_expiry(1) for these requests, but ./manage clearsessions still doesn't get rid of them.
To overcome this problem through Django, I've had to override 3 Django middlewares as I'm using SessionMiddleware, AuthenticationMiddleware and MessageMiddleware:
from django.contrib.sessions.middleware import SessionMiddleware
from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.messages.middleware import MessageMiddleware
class MySessionMiddleware(SessionMiddleware):
def process_request(self, request):
if ignore_these_requests(request):
return
super(MySessionMiddleware, self).process_request(request)
def process_response(self, request, response):
if ignore_these_requests(request):
return response
return super(MySessionMiddleware, self).process_response(request, response)
class MyAuthenticationMiddleware(AuthenticationMiddleware):
def process_request(self, request):
if ignore_these_requests(request):
return
super(MyAuthenticationMiddleware, self).process_request(request)
class MyMessageMiddleware(MessageMiddleware):
def process_request(self, request):
if ignore_these_requests(request):
return
super(MyMessageMiddleware, self).process_request(request)
def ignore_these_requests(request):
if request.POST and request.path.startswith('/api/url1/'):
return True
elif request.path.startswith('/api/url2/'):
return True
return False
Although the above works, I can't stop thinking that I may have made this more complex that it really is and that this is not the most efficient approach as 4 extra checks are made for every single request.
Are there any better ways to do the above in Django? Any suggestions would be greatly appreciated.
Dirty hack: removing session object conditionally.
One approach would be including a single middleware discarding the session object conditional to the request. It's a bit of a dirty hack for two reasons:
The Session object is created at first and removed later. (inefficient)
You're relying on the fact that the Session object isn't written to the database yet at that point. This may change in future Django versions (though not very likely).
Create a custom middleware:
class DiscardSessionForAPIMiddleware(object):
def process_request(self, request):
if request.path.startswith("/api/"): # Or any other condition
del request.session
Make sure you install this after the django.contrib.sessions.middleware.SessionMiddleware in the MIDDLEWARE_CLASSES tuple in your settings.py.
Also check that settings.SESSION_SAVE_EVERY_REQUEST is set to False (the default). This makes it delay the write to the database until the data is modified.
Alternatives (untested)
Use process_view instead of process_request in the custom middleware so you can check for the view instead of the request path. Advantage: condition check is better. Disadvantage: other middleware might already have done something with the session object and then this approach fails.
Create a custom decorator (or a shared base class) for your API views deleting the session object in there. Advantage: responsibility for doing this will be with the views, the place where you probably like it best (view providing the API). Disadvantage: same as above, but deleting the session object in an even later stage.
Make sure your settings.SESSION_SAVE_EVERY_REQUEST is set to False. That will go a long way in ensuring sessions aren't saved every time.
Also, if you have any ajax requests going to your server, ensure that the request includes the cookie information so that the server doesn't think each request belongs to a different person.
Forgive me if this has been asked repeatedly, but I couldn't find an example of this anywhere.
I'm struggling to understand how to share code among view functions in Django. For example, I want to check if the user is authenticated in many views. If they're not, I'd like to log some information about that request (IP address, etc.) then display a canned message about needing authentication.
Any advice on how to accomplish this?
You can write those code in a function, then call it in many views.
For example:
def check_login():
pass
def view1():
check_login()
pass
def view2():
check_login()
pass
This is probably best accomplished by creating a utils.py file, rather than a view. Views that don't return an HTTPResponse object are not technically valid.
See: https://docs.djangoproject.com/en/dev/intro/tutorial03/#write-views-that-actually-do-something
"Each view is responsible for doing one of two things: Returning an HttpResponse object containing the content for the requested page, or raising an exception such as Http404." ... "All Django wants is that HttpResponse. Or an exception."
Heroku will throw an error if the view does not return an HttpResponse.
What I usually do in this instance is write a function in a separate file called utils.py and import it and use it from the application files that need it.
from utils import check_login
def view1(request):
check_login(request)
pass
def view2(request):
check_login(request)
pass
One simple solution would be to use decorators just like in django's login_required, however if you need something more complex maybe you want something like class based views
I'm trying to POST some JSON and a binary file from an iPhone to a Django server running django-piston using ASIHTTPRequest
I know how to get it to work if I am ONLY sending JSON strings, and I know how to make it work if I am ONLY sending a file, but doing both is tricky.
So we'll start with ASIHTTPRequest code
ASIFormDataRequest *request = [[ASIFormDataRequest alloc] initWithURL:url];
[request setRequestMethod:#"POST"];
[request setPostFormat:ASIMultipartFormDataPostFormat];
[request appendPostData:[#"{\"save\":{\"name\":\"iostest\"}}" dataUsingEncoding:NSUTF8StringEncoding]];
[request addData:UIImageJPEGRepresentation([UIImage imageNamed:#"test.jpg"], 1.0f)
withFileName:#"test.jpg"
andContentType:#"image/jpeg"
forKey:#"data"];
[request setDelegate:self];
[request startAsynchronous];
My best idea here is that adding raw string data directly to the POST body and then adding a file just doesn't work.
But if I instead try
[request setPostValue:#"{\"name\":\"iostest\"}" forKey:#"save"];
Then the piston data dictionary will store ['save'] as a string instead of a deserialized object, so it will literally deliver the string
"{\"name\":\"iostest\"}"
Here's my Piston handler code
def create(self, request):
data = request.data
print(data['save']) #{\"name\":\"iostest\"}"
print("Files: " + request.FILES['data'].name) #test.jpg
print("Data Save Name: " + data['save']['name']) #crash, interprets this as a string indeces lookup
Ideas are welcome.
I have basically hacked my way around this.
The basic problem is that the request format in which Django expects files to be submitted to the server is one which django-piston literally just drops the ball on.
When it encounters multipart requests, it simply doesn't try to parse the data.
The solution to this problem is to manually call the parsing engine, which, in the case of JSON, is straight out of django.utils (which is kind of disappointing).
You achieve this by using ASIHTTPRequest (or the request module of your choice) to set a standard post value by key, and then access it the old fashioned way.
from django.utils import simplejson
data = simplejson.loads(request.POST['save'])
Which basically just reduces this handler method at this point to nothing more than a regular old Django view in terms of the steps you have to take to get it going.
So clearly, django-piston is not built to deal with files apparently?
My best idea here is that adding raw
string data directly to the POST body
and then adding a file just doesn't
work.
That wouldn't work, no. If you're POSTing form data using 'application/x-www-form-urlencoded' format, or 'multipart/form-data' you're not going to be able to just tack some extra data on the end - it needs to go in as part of the form data. Something like this I guess...
[request setPostValue:#"{\"save\":{\"name\":\"iostest\"}}" forKey:#"data"];
But if I remove the string data and only post the file it still doesn't work.
Is more problematic...
or if it's Piston erroneously misreading the data.
I probably wouldn't look in that direction first - piston doesn't really mess with the request object, so it seems more likely that the ASI request isn't quite right.
I think the place to start would be to inspect the incoming request and check that it really is a valid formPOST request:
Check that request["CONTENT_TYPE"] is set to 'multipart/form-data'
Inspect the request.raw_post_data and make sure that it is valid form data as specified in http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 - check that the key names are as you expected and that the file content is present. (Obviously you'll want to use a small text file when you're testing this!)
Check which keys actually are present in request.FILES, if any, in case it's as simple as something like a misnamed field.
Failing all that I'd try to narrow down if it's a problem on the client or server side by trying to write a plain python client and seeing if you have the same issue then. Looking around, something like this: http://atlee.ca/software/poster/ might be useful.