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.
Related
I'm currently learning django from the 'How to tango with django' site and i'm unable to understand the chapter dealing with forms.
Appreciate it if someone would help me out.
http://www.tangowithdjango.com/book17/chapters/forms.html
the first step is to create a forms page which maps to models.py. I seem to understand this part. I also understand that we create
a view to process the data acquired from these forms. I'm not able to understand the below code in the views page.
from rango.forms import CategoryForm
def add_category(request):
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)
else:
# If the request was not a POST, display the form to enter details.
form = CategoryForm()
How do the urlmapper know that a request method is POST or GET before the user actually enters any data in the form?
On a similar note, when would a form have a get method?
form = CategoryForm(request.POST) - would someone explain this to me? CategoryForm looks to be a class which is already inheriting from another class
what does the request.POST argument convey ?
1) The urlmapper does by default not care about GET or POST request method. It will route any request to the given view-function.
Normally, your form html-code will look like this:
<form method="post" action="some_url">
...
</form>
So, when you submit the form, the data will be send to some_url with the specified method, in this case post.
You may want to read something about when to use GET or POST, normally forms are transferred using POST.
2) form = CategoryForm(request.POST) will bind the values provided in the request's POST-dictionary to the form. You may say, it prepopulates this. This way, further working with the form (like validating it by calling form.is_valid()) will be made possible.
Perhaps you should investigate further on Django forms and modelforms by reading some official documentation.
Why do you think the URL mapper knows if it's a post it a get? It doesn't, and it doesn't care.
The thing you are missing is that this view has two responsibilities: showing the initial form (on GET) and processing the submitted form (on POST).
Your second question shows an unfamiliarity with basic Python syntax. request.POST is the parameter to the initialisation of the form instance.
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
In a previous question, I was trying to figure out the right strategy for to passing data between forms in Pyramid. Based on the answer I received, I decided the approach of using a hidden form.
I started implementing this and think there must be a better way of passing along the data. Specifically, passing parameters through the url results in a tuple that is messy to parse.
I want it to be general enough to not to know what parameters the form has and also it needs to handle file fields as well.
How I'm currently attempting to pass the form data to the confirmation page:
#view_config(renderer="templates/derived/load/error.mak", route_name='process_model_route')
def process_model(self):
#processing logic and validaton, failiure in validation sends user to error.mak
return HTTPFound(route_url('confirm_model_route', self.request, fparams=self.request.POST))
Route: config.add_route('confirm_model_route', 'rnd2/model/confirm/*fparams')
#view_config(renderer="templates/derived/confirm/model.mak", route_name='confirm_model_route')
def confirm_model(self):
form_dict = self.request.matchdict['fparams']
#need to decode and pass to template
return dict({'load_route':load_route, 'form_dict':form_dict})
The confirm/model.mak template would contain the hidden form.
The idea with this method is:
Client visits page.
Server renders the form.
Client fills in form and POSTs to URL.
Server renders a new page that contains a hidden form with all of the data it just received in the POST.
Client POSTs to a URL, confirming the submission.
Server persists the data from the hidden form and redirects.
Now depending on usability, it's up to you to decide how many different URLs you actually want here and how many views in Pyramid. You have to think about what happens with invalid data?
Notice in the outline above, once the user POSTs the form to a URL, that URL must return the confirmation page containing a hidden form. If you try to redirect the user to a confirmation page instead, you must persist the data somehow, either in a session or through the hack you showed in your example (shoving all of the data into the GET). The second solution is very bad because it abuses the true purpose of GET in HTTP.
There is also the convention that every POST should result in a redirect to avoid a client submitting the form multiple times. With this in mind you might consider the simple solution of rejecting POSTs that do not have a "confirmed" flag and simply setting the "confirmed" flag in javascript after prompting the user. This allows you to keep your form handling logic simple.
If you don't want to rely on javascript and you don't want to persist the form data in a session, then you run into the issue of not redirecting after the first POST but other than that it should be simple from the outline above.
Is there anyway to pass context variables to a redirect response? I want to redirect a user to a success page after they submit a form, but I don't want the success page to be just a static html file. I need to display extra information based on the form data.
I have looked at this question, but the solution presented there simply renders a different file at the same url. I'd like to redirect the user so that hitting refresh at the page won't submit duplicate entries into the application.
Right now the only thing I have been able to use with some success is redirecting to a url while passing it GET variables as described here. That just seems like a bit of a hack, and was just wondering if there is any better solution...
Thank You
The way I see it you have three options:
Use GET variables in the redirect.
Store something in the session.
If you are creating an object using the form that was submitted, put the id of that object in the redirect url and use it in the new view.
The limitation you are running up against is that http is stateless, not something inherent in django.
How about storing your values in a session, then have the redirected page pick up the values from there?
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']