Monday, 12 August 2013

Django QueryDict empty with request.POST but populated in request.GET

Django QueryDict empty with request.POST but populated in request.GET

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.
(Disclaimer: I'm a django beginner, and I appreciate any guidance on what
I need to learn to understand my problem and django admin better.)
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:
http://mydomain.com/some_page/another_page/
The Twilio request then looks something like the following:
http://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?

No comments:

Post a Comment