Capture all URL parameters in Django views.py? - python

There are lots of question discussing how to get 1 parameter, but how do you get all of the parameters, preserving their order?
There's this way: request.GET.get('q', '') to get 1 parameter.
I need to capture POST requests to my URL, then add a parameter to the URL, and send it right back to confirm it's validity and source. This is for PayPal IPN if you're wondering.
Thanks!

As #Daniel Roseman said you probably don't need to preserve the order, in which case you can just use the request.GET dict
Alternatively you can get the raw querystring:
request.META['QUERY_STRING']
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META

As Daniel Roseman mentions, order should not be significant between POST or GET request parameters; think of them like key-value pairs rather than a list.
If you want to maintain order, then perhaps pass a list as the value in your POST and grab it in Django:
myData = request.POST.get("myQuery")
Specifically, POST requests don't use the querystring* (see here). POSTs use the request body while GETs use the query string. Note that security-wise, this also means important client information isn't blatantly displayed in the URL -- which is especially important when dealing with payments.
Update: *Apparently, POSTs can use the query string, but they really shouldn't. See this SO post for more.

Yes, order is significant here. This is what I'm going to use:
newParameteres = 'cmd=_notify-validate&' + self.request.POST.urlencode()
req = urllib2.Request("http://www.paypal.com/cgi-bin/webscr", newParameteres)

Related

Python Requests: How to set optional and non-optional parameters at the same time?

I want to query my database with an API.
I have:
import requests
query_params = {'author':'grisham','year':'1998'}
response = requests.get(url='http://111.111.1.1:1000',params=query_params)
This works fine, I can return a data frame of all the books with author grisham published in the year 1998.
However, I want to amend this to say 'author is a compulsory parameter, but the year may optionally be added as a second parameter' (to differentiate for those who just want all his books, versus those who want to know what he published in one particular year).
I can't seem to find in the requests library, instead of params, can i change this to two dictionaries, optional and compulsory params, and pass them in separately? Or is there a better way to do this?
I guess this is sort of what I'm looking for, wondering if there is something more pythonic/efficient/cleaner for my use case?
Whether a given parameter is required or not, and (if not required) whether you choose to include it in a call to the API makes no difference to the requests library. It's job starts and ends with sending the data you give it and reporting the result. Whether year is required should be included in the API specification. If it is optional, then you can choose to leave it out by simply not including it in the dictionary you send.
Put another way, the year field is a parameter that to the API. It isn't a parameter to the requests library, and requests only knows or cares about it to the extent that it's a piece of data that is needs to send in the body of the request. To requests the year field is always optional, because requests is just passing a message along.
Like others have said, it's up to you to validate the provided parameters to decide whether any required parameter is correctly passed in. The requests library is only sending it out.
And yes, you can pass two dictionaries into requests.get() (requires Python 3.5+):
query_params = {**required_params, **optional_params}

Unable to use "Filter" in AWS Rest API request

I am trying to use "Filter" in request parameters while sending REST API request to AWS. Surprisingly, below request parameter just works:
request_parameters = 'Action=DescribeAvailabilityZones&Version=2016-11-15'
However, as soon as I change it to:
request_parameters = 'Action=DescribeAvailabilityZones&Filter.1.state=available&Version=2016-11-15'
I get, "The parameter state is not recognized"
I am picking up the Filter's syntax from here
Any suggestions please? Thanks in advance.
figured out the solution. The parameters list expects the filter to be passed in a key/value fashion. Below is the amendment which I found to be working:
request_parameters = 'Action=DescribeAvailabilityZones&Filter.1.Name=state&Filter.1.Value=available&Version=2016-11-15'
I also noticed that unless this option is present in the list of recognized filters, it wont work. This can be found here under specific Actions.
Also, filters tags bear relation with tags in XML response. for e.g. the filter to list state of an AvailabilityZone is "state" but in the XML response it is tagged as <zoneState>.

flask-restless use pagination or get full response

Sometimes I want to get from a resource the full response and sometimes I want it with pagination. Until now I was only able to use either the one or the other.
But isn't there a way to set flask-restless to use both depending on the paramters i pass on the GET request?
If I want to disable pagination for a specific resource I change the settings like this:
manager.create_api(someresource, methods=['GET'], results_per_page=None)
But now pagination is completly disabled and that's not the behaviour I wish.
And if pagination is enabled as default it returns only the first page.
Isn't there a way to tell flask-restless to get only the first page if I specifically pass the page 1 in the query string like so:
GET http://someaddress/resource?page=1
I was actually able to solve the problem using a loop but I don't think it is a nice solution because I have to use multiple requests.
I requested the resource and fetched the total_pages and then I ran a loop to total_pages and passed each iteration as an argument in the query string for another new request to fetch each page:
i = 1
while i <= response.total_pages:
page_response = requests.get("http://someurl/someresource?page=" + str(i))
...
But I don't think it is a nice way to solve that issue. If there is a possibility to change the settings on flask-restless to fetch only the first page if it is passed as an argument in the query string then I would be more than happy but if there is still another way to use both then it's also good.
You can get the behaviour you want by disabling pagination with:
manager.create_api(someresource, methods=['GET'], results_per_page=0)
And then query the API with the results_per_page parameter like so:
GET http://someaddress/resource?results_per_page=2
The results_per_page parameter has to be a positive integer and will be your new page size. The parameter is further documented here.
Getting the full response without pagination is straight forward with this configuration. Just omit the results_per_page parameter:
GET http://someaddress/resource

GET search in multilanguage site

I've included a search form in my web2py application, in the following form:
myapp/controller/search?query=myquery
However, for security reasons web2py automatically replaces spaces and non-alphanumeric characters with underscores, which is okay for English-only sites but
an impediment for languages that use accent marks. For example, searching for "áéíóú" returns five underscores.
This could be solved by using POST instead of GET for the search form, but then the users wouldn't be able to bookmark the results.
Is there any option to solve this?
Thanks in advance.
Here's an idea that I've used in the past:
Use post to submit the query
Generate a unique string (e.g. youtube: https://www.youtube.com/watch?v=jX3DuS2Ak3g)
Associate the query to that string and store as key/value pair in session/app state/db (depending on how long you want it to live)
Redirect the user to that
If you don't want to occupy extra memory/space as they tend to grow a lot in some cases, you can substitute steps 2-3 with encrypting the string to something you can decrypt afterwards. You can do this in a middleware class so that it's transparent to your app's logic.
This is a general problem people face while handling urls.
You can use the quote/quote_plus module in urllib to normalize the strings -
For example, from the strings you suggested -
>>> print urllib.quote('éíóú')
%C3%A9%C3%AD%C3%B3%C3%BA
>>> print urllib.unquote('%C3%A9%C3%AD%C3%B3%C3%BA')
éíóú
you will have to perform the unquote when you retrieve it on the backend from the request.
There are also some other posts which might be helpful - urlencode implementation and unicode ready urls

Get variable from url in view

I want to make an HTTP GET request to ip:port/this1234 and use "1234" as a variable in Python code. The "1234" is an arbitrary int. How do I get the int as an argument to my view?
#app.route('/stop####')
def stop####():
global stopped
if stopped:
call(["./stop.sh"], shell = True)
stopped = False
return "started"
return "already started"
You'll want to take a look at the Quickstart guide.
Meanwhile, you can get POST data using
myvar = request.form["myvar"]
and GET using
myvar = request.args.get("myvar")
The guide then goes on to mention some error handling recommendations and references the more in depth request object page.
We recommend accessing URL parameters with get or by catching the KeyError because users might change the URL and presenting them a 400 bad request page in that case is not user friendly.
For a full list of methods and attributes of the request object, head
over to the request documentation.
You might also want to look at routing a bit. I'm uncertain what you're trying to accomplish with the pound sign in your routing (EDIT: I see what you mean on re-reading; see edit at bottom). Note the quote below from a comment on SitePoint.
browsers don't send the #whatever part of the URL to the server in the HTTP request when requesting the page
Ok, so if you want to pass the value in the URI, I recommend something more like: example.com/this/1234 and your routing rule would look like #app.route('/this/<myVar>') above your def my_func(myVar):
Finally, at some level, killing any process based off of an http request seems awful daring, but you know your environment best, and for all I know, it might not even be exposed to the internet. Goodluck, and don't forget to be safe about this.
I think the obvious way to do it would be:
#app.route('/kill/<int:pid>')
def kill(pid):
.....

Categories

Resources