Restful API with web.py - python

In PHPs Slim I can do this:
$app->get('/table/{table}', function (Request $request, Response $response, $args) {
$table = $args['table'];
$mapper = new TableMapper($this, $table);
$res = $mapper->getTable();
return $response->withJson($res);
}
$app->get('/table/{table}/{id}', function (Request $request, Response $response, $args) {
$table = $args['table'];
$id = (int)$args['id'];
$mapper = new TableMapper($this, $table);
$res = $mapper->getTableById($id);
return $response->withJson($res);
}
Now I'm trying with web.py. I can do this:
urls = (
'/table/(.+)', 'table',
)
class table:
def GET( self, table ):
rows = db.select( table )
web.header('Content-Type', 'application/json')
return json.dumps( [dict(row) for row in rows], default=decimal_default )
but if I try to extend this by doing, e.g.:
urls = (
'/table/(.+)', 'table',
'/table/(.+)/(\d+)', 'table_by_id'
)
Processing never arrive at the second of the urls. Instead the code does a db.select on a table name which is "table/id", which of course errors.
How can I develop this to parse a url with id added?

web.py matches in order listed in urls, so switching the order is one way to solve your issue:
urls = (
'/table/(.+)/(\d+)', 'table_by_id',
'/table/(.+)', 'table'
)
Another piece of advice: Tighten up your regex, so you match more closely exactly what you're looking for. You'll find bugs sooner.
For example, you'll note your /table/(.+) will indeed match "/table/foo/1", because the regex .+ also matches /, so you might consider a pattern like ([^/]+) to match "everything" except a slash.
Finally, no need for a leading '^' or trailing '$' in your URLs, web.py always looks to match the full pattern. (Internally, it adds '^' and '$').

Try this one:
urls = (
'^/table/(.+)/$', 'table',
'^/table/(.+)/(\d+)/$', 'table_by_id'
)

Related

How to replace parameterized string?

My use-case is to return the redirection uri for the given uri.
URI's will be as follows:
/books
/books/economic-genious
/books/flight-mechanics
My regular expression to match the above URI's as follows:
/books(/(.*))?$
My destination is configured as follows: /ebooks$1. So that the above URI's will be converted to:
/ebooks
/ebooks/economic-genious
/ebooks/flight-mechanics
For this my existing Javascript code is:
function getMappedURI(uri) {
var exp = new RegExp('/books(/(.*))?$');
var destUri = '/ebooks$1';
var redirectUri = uri.replace(exp, destUri);
return redirectUri;
}
Unable to achieve the same in Python.
That's a difficult way to replace the beginning of strings. If it's all about the result I would do it this way
import re
uri_list = [
'/books',
'/books/economic-genious',
'/books/flight-mechanics',
]
def getMappedURI(uri):
return re.sub(r'^\/books', '/ebooks', uri)
for uri in uri_list:
print(getMappedURI(uri))
Result
/ebooks
/ebooks/economic-genious
/ebooks/flight-mechanics
If you need to use the original regular exprression this should work
import re
uri_list = [
'/books/',
'/books/economic-genious',
'/books/flight-mechanics',
]
def getMappedURI(uri):
return re.sub(r'\/books(\/(.*))?$', r'/ebooks\1', uri)
for uri in uri_list:
print(getMappedURI(uri))
Result
/ebooks/
/ebooks/economic-genious
/ebooks/flight-mechanics
Note that backslashes have been added before the slashes in your regular expression.
If you want to avoid that you must use
re.sub(r'/books(/(.*))?$'.replace('/', r'\/'), r'/ebooks\1', uri)

django get_or_create saves data in parentheses

I am using django's get_or_create to save the data into postgres. The code works fine but the itemgrp1hd field saves as ('Mobile 5010',) while I have only fed Mobile 5010. Can anyone explain why the parentheses & single quotes are appearing when saved in postgres.
The code is as below:
#api_view(['GET', 'POST', 'PUT', 'DELETE'])
def Post_Items_Axios(request):
data_itemfullhd = request.data['Item Name']
data_itemgrp1hd = request.data['Item Group1']
td_items, created = Md_Items.objects.get_or_create(
cunqid = entity_unqid,
itemfullhd = data_itemfullhd,
# defaults = dict(
# itemgrp1hd = data_itemgrp1hd,
# )
)
# type(request.data['Item Group1'])
# <class 'str'>
td_items.itemgrp1hd = data_itemgrp1hd,
td_items.save()
data = {'data_itemfullhd': data_itemfullhd}
return Response(data)
You must remove the trailing comma at the end of (or around, as I am on mobile) line 15.
Change
td_items.itemgrp1hd = data_itemgrp1hd,
td_items.save()
To
td_items.itemgrp1hd = data_itemgrp1hd
td_items.save()
Having a comma at the end tells Python that you want It saved in a tuple.
See this question here for more about trailing commas and tuples.
What is the syntax rule for having trailing commas in tuple definitions?

Limiting the lenght of response with python requests

I'm setting up a Views that save data from an API into my database on a button click and I'm having a hard time trying to figure out how to limit the size of the requests response for product descriptions in the following way:
If the lenght of a description is higher than 2000, delete some of the letters at the end of it until it reaches the limit of 2000, but don't remove it completely from the request.
As of now, what I've been able to achieve is to completely remove the product infos if the lenght is higher than 2000 as you can see below.
My django views function:
def api_data(request):
if request.GET.get('mybtn'): # to improve, == 'something':
resp_1 = requests.get(
"https://www.test-headout.com/api/public/v1/product/listing/list-by/city?language=fr&cityCode=PARIS&limit=5000&currencyCode=CAD",
headers={
"Headout-Auth": HEADOUT_TEST_API_KEY
})
resp_1_data = resp_1.json()
base_url_2 = "https://www.test-headout.com/api/public/v1/product/get/"
for item in resp_1_data['items']:
# concat ID to the URL string
url = '{}{}'.format(base_url_2, item['id'] + '?language=fr')
# make the HTTP request
resp_2 = requests.get(
url,
headers={
"Headout-Auth": HEADOUT_TEST_API_KEY
})
resp_2_data = resp_2.json()
if len(resp_2_data['contentListHtml'][0]['html']) < 2000: #represent the description of a product
Product.objects.get_or_create(
title=item['name'],
destination=item['city']['name'],
description=resp_2_data['contentListHtml'][0]['html'],
link=item['canonicalUrl'],
image=item['image']['url']
)
return render(request, "form.html")
But I remove way to much rows by doing this so I would like to know how can I fix this?
Please help.
Instead of using a conditional statement, you can use the slice operator to specify the character limit for the description. Using this approach you can refactor the relevant part of your code:
resp_2_data = resp_2.json()
Product.objects.get_or_create(title=item['name'],
destination=item['city']['name'],
description=resp_2_data['contentListHtml'][0]['html'][0:2000],
....
)

Django optional view parameter with HttpResponseRedirect

So I have a view that grabs a person's info from a query and returns the info to the page:
def film_chart_view(request, if_random = False):
I also have a view that randomly grabs a person's info and redirects it to the above view:
def random_person(request):
.
.
return HttpResponseRedirect(reverse('home.views.film_chart_view')+"?q="+get_person.short)
However, I want the first view to recognize if it came from the second view, so that if it is, it sets the if_random parameter to True, but I'm not exactly sure how to do that.
my urls:
url(r'^film_chart_view/$', 'home.views.film_chart_view'),
url(r'^random/$', 'home.views.random_person'),
You don't have to pass if_random as a url parameter.
def random_person(request):
return HttpResponseRedirect(
reverse('home.views.film_chart_view') + \
"?q=" + get_person.short + \
"&is_random=1"
)
def film_chart_view(request):
is_random = 'is_random 'in request.GET
But if you prefer url parameters, the solution is a little more complex.
The parameters passed to the view function comes from the url patterns, you need to set them at first.
Because the is_random para is optional, I suggest you to write 2 separated patterns for the film_chart_view.( actually you can combine these 2 patterns to one with a more complex regex expr, but readability counts.)
urlconf:
url(r'^film_chart_view/$', 'home.views.film_chart_view', name ='film_chart_view'),
url(r'^film_chart_view/(?P<is_random>.*)/$', 'home.views.film_chart_view', name ='film_chart_view_random'),
url(r'^random/$', 'home.views.random_person'),
def random_person(request):
return HttpResponseRedirect(
reverse('home.views.film_chart_view', kwargs={'is_random': '1'}) + \
"?q=" + get_person.short
)
The view parameters(except the request) are always strings, you need to convert it to int/bool/... in you code.
def film_chart_view(request, is_random=None):
if is_random:
...

Django get url regex by name

I have a case where I have defined some Django url patterns and now I want to retrieve the regular expression associated with a given pattern. I want that because I want to pass these regular expressions to the client so I can check urls in client as well ( I'm talking about browser side history manipulation ) and fire appropriate handlers ( in JavaScript ) when there is a match.
For example if I have:
# urls.py
urlpatterns = patterns("",
url(r"^$", Index.as_view(), name="index"),
url(r"^user/", include("User.urls", namespace="User")),
)
# User/urls.py
urlpatterns = patterns("",
url(r"^profile/(?P<slug>.*)$", GetProfile.as_view(), name="get_profile")
)
then I need the following function:
>>> get_regex("User:get_profile")
'^user/profile/(?P<slug>.*)$'
( or however Django translates it ). Note that I'm using namespaces. Any ideas? Django1.5.
Also I've managed to write a function that returns the urlpattern object associated with a passed name, however doing url.regex.pattern returns '^profile/(?P<slug>.*)$. So as you can see there is no leading ^user/.
There are several javascript reverse implementations out there.
http://djangojs.readthedocs.org/en/latest/djangojs.html#reverse-urls
https://github.com/version2/django-js-reverse
It's not the regex, but you could test the urls in your client code just like you do in the server, so it's even better in my opinion.
EDIT: Since you need to ignore URL arguments, you could get an idea from the source of django-js here. It already removes optional URL arguments, so it's probably very similar to what you describe.
The code iterates over every pattern removing the ?P from each argument subregex so you could just replace them with .*.
The point is you have in that source every regex you could possibly need to do your implementation. See the global patterns in lines 24-29.
So I've tried few things and finally I came up with my own solution. First I convert urlpatterns into a form which JavaScript understands:
import re
converter = re.compile(r"\?P<.*?>")
def recursive_parse(urlpatterns, lst):
for pattern in urlpatterns:
obj = {
"pattern": converter.sub("", pattern.regex.pattern)
}
if hasattr(pattern, "name") and pattern.name is not None:
obj["name"] = pattern.name
if hasattr(pattern, "namespace"):
obj["namespace"] = pattern.namespace
if hasattr(pattern, "url_patterns"):
if "urls" not in obj:
obj["urls"] = []
recursive_parse(pattern.url_patterns, obj["urls"])
lst.append(obj)
def generate_paths(urlpatterns):
paths = []
recursive_parse(urlpatterns, paths)
return paths
Then I call generate_paths(urlpatterns), JSON-stringify the result and pass it to JavaScript (note that in JavaScript I have to convert regular expressions as strings to RegExp objects). In JavaScript I have
var recursive_check = function(url, patterns, names, args) {
var l = patterns.length;
for (var i = 0; i < l; i++) {
var pat = patterns[i],
match = pat.pattern.exec(url);
pat.lastIndex = 0;
if (match) {
names.push(pat.namespace || pat.name);
var f = match.shift(),
url = url.replace(f, ""),
ml = match.length;
for (var j = 0; j < ml; j++) {
args.push(match[j]);
}
if (pat.urls) {
recursive_check(url, pat.urls, names, args);
}
break;
}
}
};
var fire_handler = function(url) {
var names = [], args = [];
recursive_check(url, patterns, names, args);
// do something...
};
Now in // do something... I can do something with names and args. For example I can keep a dictionary of named handlers, I can search for a handler (based on names) and call it with args.
That's the solution that works for me. Converting urlpatterns to JavaScript patterns might not be perfect (since converter seems to be a bit too simplified) but it works in most simple cases.
Try this:
from django.core.urlresolvers import get_resolver
resolver = get_resolver(None)
url = resolver.reversed_dict.getlist('get_profile')
if url:
pattern = url[0][1]
Not an answer but might be useful to someone else looking at this.
The following generates a list of all, complete url patterns in the Django project, including for nested URLRegexResolvers, based on #Freakish's code.
import re
from django.core.urlresolvers import get_resolver
converter = re.compile(r"\?P<.*?>")
def trim_leading_caret(s):
return s[1:] if s.startswith('^') else s
def recursive_parse(urlpatterns, lst, prefix=None):
for pattern in urlpatterns:
path = (prefix or '') + trim_leading_caret(converter.sub("", pattern.regex.pattern))
if hasattr(pattern, "url_patterns"):
recursive_parse(pattern.url_patterns, lst, path)
else:
lst.append('^' + path)
def generate_paths(urlpatterns):
paths = []
recursive_parse(urlpatterns, paths)
return paths
generate_paths(get_resolver(None))
As far as I understood, you want to be able to return the regex expression (and not the url) of a given view.
This is my sketch of solution:
The function url returns an instance of RegexURLResolver. This class does store the regex, because it calls LocaleRegexProvider on __init__ (in this line and this line).
So, I think that you can
reverse search the view plus namespace
get the tuple of that view from the tuple of tuples urlpatterns
return _regex of the first argument, LocaleRegexProvider._regex (or regex()), of the tuple respective to the view.
I'm not sure this works (didn't tested), neither that it is the best solution, but at least you have some links on where Django stores the regex.

Categories