Does anyone have any simple code samples for Django + SWFUpload? I have it working perfectly in my PHP application but Django is giving me headaches.
Unfortunately I can't give you any very detailed code samples, but I have quite a bit of experience with working with SWFUpload + Django (for a photo sharing site I work on). Anyway, here are a few pointers that will hopefully help you on your quest for DjSWF happiness :)
You'll want to use the cookies plugin (if of course you are using some sort of session-based authentication [like django.contrib.auth, and care who uploaded what).
The cookies plugin sends the data from cookies as POST, so you'll have to find some way of getting this back into request.COOKIES (process_request middleware that looks for a settings.SESSION_COOKIE_NAME in request.POST on specific URLs and dumps it into request.COOKIES works nicely for this :)
Also, remember that you must return something in the response body for SWFUpload to recognize it as a successful upload attempt. I believe this has changed in the latest beta of SWFUpload, but anyway it's advisable just to stick something in there like 'ok'. For failures, make use of something like HttpResponseBadRequest or the like.
Lastly, in case you're having trouble finding them, the uploaded file is in request.FILES :)
If you have anything perplexing I haven't covered, feel free to post something more detailed and I'll be happy to help.
Django version of the samples for SWFUpload:
http://github.com/naltimari/django-swfupload-samples/tree/master
So long uploadify. Great idea but it is just buggy, especially on Windows.
The following is my Django-specific implementation for fixing this issue (i.e. my uploads were failing in Firefox with a 302 Redirect).
In my initial view that generates the page with the uploader on it, I looked at the cookies and found sessionid
ipdb> self.request.COOKIES
{'csrftoken': '43535f552b7c94563ada784f4d469acf', 'sessionid': 'rii380947wteuevuus0i5nbvpc6qq7i1'}
When I looked at what was being posted in the SWFUploadMiddleware (when using Firefox), I found that the sessionid was not being set.
In my intial view that generates the page that contains the upload handler, I added the sessionid to the context.
context['sessionid'] = self.request.session.session_key
In my swfuploader settings, I added sessionid to the post-params option as follows:
post_params: {... "sessionid": "{{ sessionid }}" ...},
Now, when I looked in the SWFUploadMiddleware, I could see the sessionid being posted, and my uploads started working if Firefox.
ipdb> request.POST
<QueryDict: {... u'session_id': [u'rii380947wteuevuus0i5nbvpc6qq7i1'],...}>
For completeness, my SWFUploadMiddleware looks like this...
from django.conf import settings
from django.core.urlresolvers import reverse
class SWFUploadMiddleware(object):
def process_request(self, request):
if (request.method == 'POST') and (request.path == reverse('upload_handler')) and request.POST.has_key(settings.SESSION_COOKIE_NAME):
request.COOKIES[settings.SESSION_COOKIE_NAME] = request.POST[settings.SESSION_COOKIE_NAME]
# http://stackoverflow.com/questions/6634666/403-forbidden-error-on-swfupload-and-django
# Fix for problem uploading images (403 error) in Firefox 20 and others
if request.POST.has_key('csrftoken'):
request.COOKIES['csrftoken'] = request.POST['csrftoken']
Related
I am building a simple blog using Web2Py on Debian Linux.
I have a controller called blog.py, to which I added the following function, along with an if block:
def display_form():
form = SQLFORM(db.blog)
if form.process().accepted:
session.flash = 'form accepted'
redirect(URL('thanks'))
elif form.errors:
response.flash = 'form has errors'
else:
response.flash = 'please fill out the form'
return locals()
I proceeded to add a "view" html file called blog/display_form.html, with a basic template, as follows:
{{extend 'layout.html'}}
<h1>Display Form</h1>
{{=form}}
I load the "display_form" blog page just fine, and it accepts all the input successfully, but it does not redirect to a thank you page. Instead, the browser generates an "invalid function blog/thanks" error.
I tried removing the compiled app via the Web2Py admin interface, and recompiled everything. Still does not work. I added a "view" for the "Thanks" page, but that does not change anything. I restarted the Web2Py framework and the web server, but still no go.
Some web sites refer to a possible routes.py issue, but I am confused as to why that would be pertinent at all.
Please help,
I am hitting a brick wall here.
So, after tweaking a number of things, and removing all of the compiled files, and starting from scratch again, the solution turned out to be way more simple than I was trying to make it.
I simply defined a function called thanks in the aforementioned blog.py controller, and returned the local variables, like so:
def thanks():
return locals()
I then added a blog/thanks view file, with a basic html header, stating:
Thank you for submitting the form!
And it finally redirected the display_form blog page as intended to a thanks page, thereby flashing the form accepted message (also as expected).
Thanks for your help, Anthony!
Cheers.
When you pass only a single argument to the URL() helper, it assumes that argument is the function name and that the controller is the current controller. Since the current controller is blog, URL('thanks') will generate the following URL: /yourapp/blog/thanks.
Presumably, your thanks function is in a different controller (perhaps default.py), in which case, a request for the above URL will generate the error you observe. Just change the URL to URL('default', 'thanks').
Short version: On a django site, I can grab values from request.GET but not request.POST in response to a request from Twilio. I suspect it has something to do with csrf, but I'm not really sure how to debug the problem. Details below.
Long version:
I am helping a friend with a project where we are running a medical survey over SMS using the Twilio REST API. I had a domain and a very bare-bones django-built site on that domain, which I had built really just to better familiarize myself with django, so we're using that.
We're collecting SMS responses to our survey and as part of the Twilio API, it sends any response to our number to a url specified under the account, so we have the response targeting something like the following:
...mydomain.com/some_page/another_page/
The Twilio request then looks something like the following:
...mydomain.com/some_page/another_page/?AccountSid=###SOME_LONG_ACCOUNT_SIDE&From=%2BPHONE_NUMBER&Body=bla+BLA+bla+BLA&SmsSid=##MESSAGE_ID_KEY&SmsMessageSid=##MESSAGE_ID_KEY&FromCity=Santa+Cruz&FromState=California...
Working Code
I am testing that the incoming request has our AccountSid inside it (compared with the value in the database) and in my views.py for the app, I have something that looks like the following (and this works):
from our_app import TwilioAccount
our_account = TwilioAccount.objects.get(id=1)
def twilio_response(request):
assert request.GET.get('AccountSid', None) == our_account.account_sid
## log the incoming request to the database under survey responses...
Non-Working Code
If I log-in to our Twilio account and switch the request method to POST, and then I switch all of my data collecting to request.POST, the above assert statement fails. Further debugging reveals that my QueryDict is empty under POST, POST: {}, so there is no key value grabbed.
I thought this maybe was because POST under django requires a csrf_token, but I figured checking for the AccountSid was fairly good, so I imported csrf_exempt and wrapped the above function with that:
#csrf_exempt
def twilio_response(request):
assert request.POST.get('AccountSid', None) == our_account.account_sid
## log the incoming request to the database under survey responses...
AssertionError: ...
This does not work with the exact same request: the QueryDict is empty.
Questions:
1) Is there something else I need to do to make my #csrf_exempt work? Alternate question: is this a terrible and dumb way to do this? How do people usually satisfy this requirement when working with other company's APIs and not actual, logged-in users?
1a) Instead of making it csrf_exempt, I could just keep it as a GET request, knowing that it's still checking all incoming requests against our account_sid. Should I do that or is that a really naive way to do it?
2) I am eager to learn the best way to do this: should I build a django form and then route the request to my form and test validity and clean the data that way? If so, can anyone give me a loose outline on what the view/form would look like (complete with csrf_token) when there's not going to be a template for the form?
Matt from the Twilio Developer Evangelist team here.
1) Wrapping your twilio_response function with #csrf_exempt to remove the Django CSRF token check is the right way to go here. Twilio does not generate a Django CSRF token. Instead, there are other ways to validate POSTs are coming from Twilio, such as signature validation with the X-Twilio-Signature header. See the Twilio security docs for details.
1a) Using the GET request is convenient for testing and debugging, but POST should be used in production. GET requests do not have a body according to the HTTP spec, so the results are passed in the query string. If the parameters are too large, for example with a text message that has a maximum length of 1600 characters, the query string in the URL could exceed the maximum length of a URL and potentially cause issues when you handle the string.
2) Django forms are a good way to go for this use case, particularly a ModelForm that leverages your existing Model used to save the response. For example, your ModelForm could
look something like the following if you are saving your data to a TwilioMessage model:
from django.forms import ModelForm
from .models import TwilioMessage
class MessageForm(ModelForm):
pass
class Meta:
model = ReactionEvent
# include all fields you're saving from the form here
fields = ['body', 'to', 'from_', 'signature',]
In my option,Have you open DEBUG(= True) in settings.py?
And in the DEBUG mode, you can raise An Exception(raise Exception('')),And then you can see the Environment Varialbe like url, GET,POST.etc....in you page
OR return HttpResponse(request.POST.dict())
to see the post variable.
use crsf_exempt is a right way when post
I have a similar problem to those listed in
Django produces blank pages when POST data is sent and Models are accessed
and
Nginx connection reset, response from uWsgi lost
Here is one of the views in question:
#transaction.commit_on_success
#occ_update
#checks_status
def hold(request):
if not request.user.has_perm('orders.hold'):
return error_response_rollback(NO_PERMISSION_MSG % "hold orders")
order = Order.objects.get(pk=request.POST.get('pk'))
occ_revision = int(request.POST.get('occ_revision'))
agent = Agent.get_agent(request.user)
action = Action(agent=agent, type='hold_order',
comments=request.POST.get('comments'))
action.save()
order.hold(action, occ_revision)
return ok_response_commit("Order held successfully.")
error_response_rollback rolls back the transaction and returns an HttpResponse with JSON as its contents.
I am adding permission checking to many of my views in my application and when the user does not have the correct permission, a blank response is returned.
However like the questions referenced above, if you put a
print request
or
request.POST
statement BEFORE the permission check, the NO_PERMISSION_MSG JSON string is returned to the browser correctly every time (error_response_rollback returns an HttpResponse object with JSON in it.)
You get blank responses when you check permissions before the "print request" and they do not have the correct permissions.
You do NOT get blank responses when:
the user has the correct permissions
a "print request" statement is before any permission check
you use Firefox at any point.
The #occ_update and #checks_status decorators just catch exceptions. These problems occur with and without them present.
I'm developing in Chrome and none of this is an issue in Firefox.
One page I found suggested overloading the WSGIRequest object to read the request before it is passed to the view but this seems icky to me and I'd rather find out the real solution.
Does anyone know of any fixes/settings to the runserver command to help this issue without hacking on the request? My users are primarily using Chrome so I'd prefer to keep using it... we'll see. Currently developing in Windows using Django 1.3.1
One option I have considered is making another manage.py command to handle this but that, too, seems icky.
Thanks
Update:
I have been able to re-organize my code so that any permission checks happen after some bit of data is read from the POST. This seems to have eliminated any symptoms of this problem. It's still not ideal but it is a good alternative to inserting middleware to read the post. and won't always be possible in all applications.
Please comment if you have a similar situation and just can't figure it out.
As saying in the second link in your post, especially http://forum.nginx.org/read.php?2,196581 : when you works w/ Nginx and uWSGI and get a non-empty POST, always read the request.POST before return an HttpResponse. The reason is described in the link.
You don't have to override an handler, just put the request.POST line before the return code, or inside some decorator or middleware.
I encountered this issue for production site half a year ago and put the line in a middleware to solve it.
I must submit two forms at once and I don't want javascript. So I think I can post the form to myself, do my work and then post it on to the third-party payment provider. This is to make the order appear in our datastore. The manual says how to submit a form over HTTP POST but I don't know how to direct the user's browser to this page. Is there a way? Instead of posting the form directly to the third party I thought doing something like this:
def post(self):
import urllib
#do my work
params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
f = urllib.urlopen("http://www.thirdparty.com/cgi-bin/query", params)
#direct the user to the reponse page but how?
It will work to submit the form but the user must be take to the page. Is there a way to achieve this?
Thanks for any help
There is something you can do like this:
Get the post form, do stuff on your server, and then return a redirect page that contains a form with same params but action to the remote target.
Then, you can use little js to submit the form automatically, and give something like "Not redirecting? click here" to the user.
So it's not working as what you expected, and it used js too, but I did not find any better way to handle this job. Hope this can help in any way.
You could use 307 to redirect, which preserves all the POST parameters (you'll need to make sure the POST params your servlet uses are the same as the ones your remote server uses).
self.response.set_status(307)
self.response.headers['Location'] = "http://www.thirdparty.com/cgi-bin/query"
However, not all browsers implement 307 correctly. See here: Response.Redirect with POST instead of Get?
That response is old, so maybe more browsers handle 307 correctly now.
I was hoping that Django had a built in way of getting the last url that was visited in the app itself. As I write this I realize that there are some complications in doing something like that (excluding pages that redirect, for example) but i thought I'd give it a shot.
if there isn't a built-in for this, what strategy would you use? I mean other than just storing a url in the session manually and referring to that when redirecting. That would work, of course, but I hate the thought of having to remember to do that for each view. Seems error-prone and not very elegant.
Oh, and I'd rather not depend on server specific referral environment variables.
No, there is nothing like that built in to Django core (and it's not built in because it isn't a common usage pattern).
Like Javier suggested, you could make some middleware which does what you want. Something like this:
class PreviousURLMiddleware(object):
def process_response(request, response):
if response.status_code == 200:
request.session['previous_url'] = request.get_full_url()
return response
This middleware would have to go after SessionMiddleware in your settings to ensure that the session will be updated after this (the docs have a pretty picture explaining why this is).
you can just write a middleware that does exactly that, no need to repeat logging code on every view function.