I have an URL such as: http://example.com/page/page_id
I want to know how to get the page_id part from url in the route. I am hoping I could devise some method such as:
#route('/page/page_id')
def page(page_id):
pageid = page_id
It's pretty straightforward - pass the path parameter in between angle brackets, but be sure to pass that name to your method.
#app.route('/page/<page_id>')
def page(page_id):
pageid = page_id
# You might want to return some sort of response...
You should use the following syntax:
#app.route('/page/<int:page_id>')
def page(page_id):
# Do something with page_id
pass
You can specify the ID as integer :
#app.route('/page/<int:page_id>')
def page(page_id):
# Replace with your custom code or render_template method
return f"<h1>{page_id}</h1>"
or if you are using alpha_num ID:
#app.route('/page/<username>')
def page(username):
# Replace with your custom code or render_template method
return f"<h1>Welcome back {username}!</h1>"
It's also possible to not specify any argument in the function and still access to URL parameters :
# for given URL such as domain.com/page?id=123
#app.route('/page')
def page():
page_id = request.args.get("id") # 123
# Replace with your custom code or render_template method
return f"<h1>{page_id}</h1>"
However this specific case is mostly used when you have FORM with one or multiple parameters (example: you have a query :
domain.com/page?cars_category=audi&year=2015&color=red
#app.route('/page')
def page():
category = request.args.get("cars_category") # audi
year = request.args.get("year") # 2015
color = request.args.get("color") # red
# Replace with your custom code or render_template method
pass
Good luck! :)
Related
I am using Django DRF's CursorPagination for lazy loading of my data, and currently my goal is to sort the data by more than one field.
This is how my code looks like now:
class EndlessPagination(CursorPagination):
ordering_param = ''
def set_ordering_param(self, request):
self.ordering = request.query_params.get(self.ordering_param, None)
if not self.ordering:
raise ValueError('Url must contain a parameter named ' +
self.ordering_param)
if self.ordering.startswith("\"") or self.ordering.endswith("\""):
raise ValueError('Ordering parameter should not include quotation marks'
def paginate_queryset(self, queryset, request, view=None):
# This function is designed to set sorting param right in the URL
self.set_ordering_param(request)
return super(EndlessPagination, self).paginate_queryset(queryset, request, view)
This code works fine for urls like my_url/sms/270380?order_by=-timestamp, but what if I want to sort by several fields ?
Use str.split() to split the url params
class EndlessPagination(CursorPagination):
ordering_param = 'order_by'
def set_ordering_param(self, request):
ordering_param_list = request.query_params.get(self.ordering_param, None)
self.ordering = ordering_param_list.split(',')
# here, "self.ordering" will be a "list", so, you should update the validation logic
"""
if not self.ordering:
raise ValueError('Url must contain a parameter named ' +
self.ordering_param)
if self.ordering.startswith("\"") or self.ordering.endswith("\""):
raise ValueError('Ordering parameter should not include quotation marks'
"""
def paginate_queryset(self, queryset, request, view=None):
# This function is designed to set sorting param right in the URL
self.set_ordering_param(request)
return super(EndlessPagination, self).paginate_queryset(queryset, request, view)
Example URLs
1. my_url/sms/270380?order_by=-timestamp
2. my_url/sms/270380?order_by=-timestamp,name
3. my_url/sms/270380?order_by=-name,foo,-bar
UPDATE-1
First of all thanks to you for giving a chance to dig deep :)
As you said, me too didn't see comma seperated query_params in popular APIs. So, Change the url format to something like,my_url/sms/270380??order_by=-name&order_by=foo&order_by=-bar
At this time, the request.query_params['order_by'] will be a list equal to ['-name','foo','-bar']. So, you don't want to use the split() function, hence your set_ordering_param() method become,
def set_ordering_param(self, request):
self.ordering = request.query_params.get(self.ordering_param, None)
#...... your other validations
For example, if the user were to go to localhost:8080/foobar228, I wouldn't need to hardcode in the request for foobar228 page by defining a function. I want to simply have a function that gives you the requested URL ("foobar228") and other information like GET and POST requests, and returns the HTML to be served. Any way to do this?
Use the default method. Here is an example.
import cherrypy
class Root:
#cherrypy.expose
def default(self, *url_parts, **params):
req = cherrypy.request
print(url_parts)
body = [
'Page served with method: %s' % req.method,
'Query string from req: %s' % req.query_string,
'Path info from req: %s' % req.path_info,
'Params (query string + body for POST): %s' % params,
'Body params (only for POST ): %s' % req.body.params
]
if url_parts: # url_parts is path_info but divided by "/" as a tuple
if url_parts[0] == 'foobar228':
body.append( 'Special foobar page')
else:
body.append('Page for %s' % '/'.join(url_parts))
else:
body.append("No path, regular page")
return '<br>\n'.join(body)
cherrypy.quickstart(Root())
The url segments becomes the positional arguments and any query string (?foo=bar) are part of the keyword arguments of the method, also the body parameters for the POST method are included in the keyword arguments (in this case under the name params in the method definition.
The special _cp_dispatch method
_cp_dispatch is a special method you declare in any of your controller to massage the remaining segments before CherryPy gets to process them. This offers you the capacity to remove, add or otherwise handle any segment you wish and, even, entirely change the remaining parts.
import cherrypy
class Band(object):
def __init__(self):
self.albums = Album()
def _cp_dispatch(self, vpath):
if len(vpath) == 1:
cherrypy.request.params['name'] = vpath.pop()
return self
if len(vpath) == 3:
cherrypy.request.params['artist'] = vpath.pop(0) # /band name/
vpath.pop(0) # /albums/
cherrypy.request.params['title'] = vpath.pop(0) # /album title/
return self.albums
return vpath
#cherrypy.expose
def index(self, name):
return 'About %s...' % name
class Album(object):
#cherrypy.expose
def index(self, artist, title):
return 'About %s by %s...' % (title, artist)
if __name__ == '__main__':
cherrypy.quickstart(Band())
Notice how the controller defines _cp_dispatch, it takes a single argument, the URL path info broken into its segments.
The method can inspect and manipulate the list of segments, removing any or adding new segments at any position. The new list of segments is then sent to the dispatcher which will use it to locate the appropriate resource.
In the above example, you should be able to go to the following URLs:
http://localhost:8080/nirvana/
http://localhost:8080/nirvana/albums/nevermind/
The /nirvana/ segment is associated to the band and the /nevermind/ segment relates to the album.
To achieve this, our _cp_dispatch method works on the idea that the default dispatcher matches URLs against page handler signatures and their position in the tree of handlers.
taken from docs
I am pretty new to django and django-rest-framework, but I am trying to pass lists into url parameters to then filter my models by them.
Lets say the client application is sending a request that looks something like this...
url: "api.com/?something=string,string2,string3&?subthings=sub,sub2,sub3&?year=2014,2015,2016/"
I want to pass in those parameters "things", "subthings", and "years" with their values.
Where the url looks something like this?
NOTE: Trick is that it won't be always an array of length 3 for each parameter.
Can someone point me in the right direction for how my url regex should be handing the lists and also retrieving the query lists in my views.
Thanks!
To show how I did this thanks to the document links above.
Note: I used pipes as my url delimiter and not commas -> '|'.
in my urls.py
url(r'^$', SomethingAPIView.as_view(), name='something'),
in my views.py
class SomethingAPIView(ListAPIView):
# whatever serializer class
def get_queryset(self):
query_params = self.request.query_params
somethings = query_params.get('something', None)
subthings = query_params.get('subthing', None)
years = query_params.get('year', None)
# create an empty list for parameters to be filters by
somethingParams = []
subthingsParams = []
yearParams = []
# create the list based on the query parameters
if somethings is not None:
for something in somethings.split('|'):
countryParams.append(int(something))
if subthings is not None:
for subthing in subthings.split('|'):
subthingsParams.append(int(subthing))
if years is not None:
for year in years.split('|'):
yearParams.append(int(year))
if somethings and subthings and years is not None:
queryset_list = Model.objects.all()
queryset_list = queryset_list.filter(something_id__in=countryParams)
queryset_list = queryset_list.filter(subthing_id__in=subthingsParams)
queryset_list = queryset_list.filter(year__in=yearParams)
return queryset_list
I do need to check for an empty result if they are not valid. But here is starting point for people looking to pass in multiple values in query parameters.
A valid url here would be /?something=1|2|3&subthing=4|5|6&year=2015|2016.
Checkout this doc http://www.django-rest-framework.org/api-guide/filtering/
Query params are normally not validated by url regex
def images_custom_list(args, producer_data):
tenant, token, url = producer_data
url = url.replace(".images", ".servers")
url = url + '/' + 'detail'
output = do_request(url, token)
output = output[0].json()["images"]
custom_images_list = [custom_images for custom_images in output
if custom_images["metadata"].get('user_id', None)]
temp_image_list = []
for image in custom_images_list:
image_temp = ( { "status": image["status"],
"links": image["links"][0]["href"],
"id": image["id"], "name": image["name"]} )
temp_image_list.append(image_temp)
print json.dumps(temp_image_list, indent=2)
def image_list_detail(args, producer_data):
tenant, token, url = producer_data
url = url.replace(".images", ".servers")
uuid = args['uuid']
url = url + "/" + uuid
output = do_request(url, token)
print output[0]
I am trying to make the code more efficient and clean looking by utilizing the Python's function decoration. Since these 2 functions share the same first 2 lines, how could I make a function decorator with these 2 lines and have these 2 functions be decorated it?
here's a way to solve it:
from functools import wraps
def fix_url(function):
#wraps(function)
def wrapper(*args, **kwarg):
kwarg['url'] = kwarg['url'].replace(".images", ".servers")
return function(*args, **kwarg)
return wrapper
#fix_url
def images_custom_list(args, tenant=None, token=None, url=None):
url = url + '/' + 'detail'
output = do_request(url, token)
output = output[0].json()["images"]
custom_images_list = [custom_images for custom_images in output
if custom_images["metadata"].get('user_id', None)]
temp_image_list = []
for image in custom_images_list:
image_temp = ( { "status": image["status"],
"links": image["links"][0]["href"],
"id": image["id"], "name": image["name"]} )
temp_image_list.append(image_temp)
print json.dumps(temp_image_list, indent=2)
#fix_url
def image_list_detail(args, tenant=None, token=None, url=None):
uuid = args['uuid']
url = url + "/" + uuid
output = do_request(url, token)
print output[0]
sadly for you, you may notice that you need to get rid of producer_data, but have it split in multiple arguments because you cannot factorize that part of the code, as you'll anyway need to split it again in each of the functions. I chose to use keyword arguments (by setting a default value to None), but you could use positional arguments as well, your call.
BTW, note that it's not making the code more efficient, though it's helping in making it a bit more readable (you know that you're changing the URL the same way for both methods, and when you fix the URL changing part, it's done the same way everywhere), but it's making 2 more function calls each time you call the function, so it's in no way more "efficient".
N.B.: It's basically based over #joel-cornett's example (I wouldn't have used #wraps otherwise, just plain old double function decorator), I just specialized it. (I don't think he deserves a -1)
Please at least +1 his answer or accept it.
But I think a simpler way to do it would be:
def fix_url(producer_data):
return (producer_data[0], producer_data[1], producer_data[2].replace(".images", ".servers"))
def images_custom_list(args, producer_data):
tenant, token, url = fix_url(producer_data)
# stuff ...
def image_list_detail(args, producer_data):
tenant, token, url = fix_url(producer_data)
# stuff ...
which uses a simpler syntax (no decorator) and does only one more function call.
Like this:
from functools import wraps
def my_timesaving_decorator(function):
#wraps(function)
def wrapper(*args, **kwargs):
execute_code_common_to_multiple_function()
#Now, call the "unique" code
#Make sure that if you modified the function args,
#you pass the modified args here, not the original ones.
return function(*args, **kwargs)
return wrapper
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:
...