I want to have a RESTful api which looks like this:
example.com/teams/
example.com/teams/<team_id>
example.com/teams/<team_id>/players
example.com/teams/<team_id>/players/<player_id>
...
example.com/teams/<team_id>/players/<player_id>/seasons/<season_id>/etc
Where each URI can appropriately handle GETs and possibly POSTs.
I would like to be able to do something like:
class Team(Resource):
def post(self):
#Handler for /teams/
def post(self, team_id):
#Handler for /teams/team_id
def post(self, team_id, player_id):
#Handler for /teams/team_id/players/player_id
and using:
api.add_resource(Team, '/teams/', 'teams/<team_id>/players/<player_id>')
Which won't work because the subsequent POST handlers overwrite the previous.
What is the right way with Flask-RESTful to handle an API where there may be a variable number of variables (variable depth of hierarchy) in the URL?
Python does not support method overloading in that particular way. In your code you are not overloading the post() function, you are redefining it
Basically the last definition of post() is what counts, which takes 3 parameters as you can see:
class Team(Resource):
def post(self, team_id, player_id):
# This is the final definition of post()
# The definitions above this one do not take effect
Otherwise it's pretty easy to get the behavior with a single method that has default values for the parameters:
class Team(Resource):
def post(self, team_id=None, player_id=None):
if team_id is None and player_id is None:
# first version
if team_id is not None and player_id is None:
# second version
if team_id is not None and player_id is not None:
# third version
For your URL, Flask will pass in None for the parameter that isn't defined in the URL.
Related
I have below code
#ns.route('/blah/<query>', methods=['GET'])
#api.doc(params={'query': 'Search ID'})
class myClass(Resource):
#api.doc('blah', responses={ 200: 'OK' })
def get(self, query):
"""Returns list of blah."""
which giving me below result:
question is: how can i make Get parameter value not mandatory?
I'm not positive that this is still the case and I'll be the first to admit that I'm new to this particular framework, but based on the answers to this bug from 2013, it does not appear that the framework allows for this kind of behavior.
The recommended workaround is to create two Resources, one with the parameter and one without. That's the workaround I've used; usually pairing it with a simple base class with a method and an optional parameter that both call.
An example from the GitHub Issue:
class Users(Resource):
def get(self):
return users
def post(self):
#...
class User(Resource):
def get(self, id=None):
return find_user_by_id(id)
def post(self):
#...
api.add_resource(Users, '/users')
api.add_resource(User, '/users/<id>')
I've been using Flask-Classy for my server, and it works great. However, I've come across a use case that I haven't seen written about, but it's a pretty common use case, so I'd be shocked if it's not possible.
I have two APIs which I want to nest, and by that I mean I have:
class UsersView(FlaskView):
decorators = [jwt_required()]
route_prefix = '/api/v1/'
def index(self):
...
which is located at http://example.com/api/v1/users and I can access user 1 via http://example.com/api/v1/users/1
Now, how would I write the FlaskView which would let me do something like this?
http://example.com/api/v1/users/1/devices/3
When I try embedding the resource id in the route_prefix, I get a keyword argument error:
class DevicesView(FlaskView):
decorators = [jwt_required()]
route_prefix = '/api/v1/users/<user_id>/'
def index(self):
...
TypeError: index() got an unexpected keyword argument 'user_id'
One last point is that I can, naturally, use kwargs:
route_prefix = '/api/v1/users/<user_id>/'
def test(self, **kwargs):
print kwargs['user_id']
http://example.com/api/v1/users/103/devices will spit out '103', however, using kwargs feels kinda hokey. Is there a better way?
I put the top-level placeholder in the route_base in the register call.
For example:
class UsersView(FlaskView):
#route('/', methods=['GET'])
def index(self):
pass
class DevicesView(FlaskView):
#route('/', methods=['GET'])
def index(self, user_id):
pass
UsersView.register(app, route_base='/users', trailing_slash=False)
DevicesView.register(app, route_base='/users/<user_id>/devices', trailing_slash=False)
Now the user_id comes in as the first parameter on every method in DevicesView.
In case you haven't found a solution as yet...The answer is pretty simple here you need to have the index defined as:
def index(self, user_id):
you have to do this since you would want to know the base resource through which to access the required resource. In your example devices index would give me a list of all devices belonging to the user. In order to get this information you will first need to know which user's devices are being asked for
I'm building a rate-limiting decorator in flask using redis stores that will recognize different limits on different endpoints. (I realize there are a number of rate-limiting decorators out there, but my use case is different enough that it made sense to roll my own.)
Basically the issue I'm having is ensuring that the keys I store in redis are class-specific. I'm using the blueprint pattern in flask, which basically works like this:
class SomeEndpoint(MethodView):
def get(self):
# Respond to get request
def post(self):
# Respond to post request
The issue here is that I want to be able to rate limit the post method of these classes without adding any additional naming conventions. In my mind the best way to do this would be something like this:
class SomeEndpoint(MethodView):
#RateLimit # Access SomeEndpoint class name
def post(self):
# Some response
but within the decorator, only the post function is in scope. How would I get back to the SomeEndpoint class given the post function? This is the basic layout of the decorator. That might be confusing, so here's a more concrete example of the decorator.
class RateLimit(object):
"""
The base decorator for app-specific rate-limiting.
"""
def __call__(self, f):
def endpoint(*args, **kwargs):
print class_backtrack(f) # Should print SomeEnpoint
return f(*args, **kwargs)
return endpoint
basically looking for what that class_backtrack function looks like. I've looked through the inspect module, but I haven't found anything that seems to accomplish this.
You can decorate the entire class instead of just the methods:
def wrap(Class, method):
def wrapper(self, *args, **kwargs):
print Class
return method(self, *args, **kwargs)
return method.__class__(wrapper, None, Class)
def rate_limit(*methods):
def decorator(Class):
for method_name in methods:
method = getattr(Class, method_name)
setattr(Class, method_name, wrap(Class, method))
return Class
return decorator
#rate_limit('post')
class SomeEndpoint(object):
def post(self):
pass
class Subclass(SomeEndpoint):
pass
a = Subclass()
a.post()
# prints <class 'SomeEndpoint'>
I'm using tastypie and I want to create a Resource for a "singleton" non-model object.
For the purposes of this question, let's assume what I want the URL to represent is some system settings that exist in an ini file.
What this means is that...:
The fields I return for this URL will be custom created for this Resource - there is no model that contains this information.
I want a single URL that will return the data, e.g. a GET request on /api/v1/settings.
The returned data should return in a format that is similar to a details URL - i.e., it should not have meta and objects parts. It should just contain the fields from the settings.
It should not be possible to GET a list of such object nor is it possible to perform POST, DELETE or PUT (this part I know how to do, but I'm adding this here for completeness).
Optional: it should play well with tastypie-swagger for API exploration purposes.
I got this to work, but I think my method is kind of ass-backwards, so I want to know what is the common wisdom here. What I tried so far is to override dehydrate and do all the work there. This requires me to override obj_get but leave it empty (which is kind of ugly) and also to remove the need for id in the details url by overriding override_urls.
Is there a better way of doing this?
You should be able to achieve this with the following. Note I haven't actually tested this, so some tweaking may be required. A more rich example can be found in the Tastypie Docs
class SettingsResource(Resource):
value = fields.CharField(attribute='value', help_text='setting value')
class Meta:
resource_name = 'setting'
fields = ['value']
allowed_methods = ['get']
def detail_uri_kwargs(self, bundle_or_obj):
kwargs = {}
return kwargs
def get_object_list(self, request):
return [self.obj_get()]
def obj_get_list(self, request=None, **kwargs):
return [self.obj_get()]
def obj_get(self, request=None, key=None, **kwargs):
setting = SettingObject()
setting.value = 'whatever value'
return setting
The SettingObject must support the getattr and setattr methods. You can use this as a template:
class SettingObject(object):
def __init__(self, initial=None):
self.__dict__['_data'] = {}
if initial:
self.update(initial)
def __getattr__(self, name):
return self._data.get(name, None)
def __setattr__(self, name, value):
self.__dict__['_data'][name] = value
def update(self, other):
for k in other:
self.__setattr__(k, other[k])
def to_dict(self):
return self._data
This sounds like something completely outside of TastyPie's wheelhouse. Why not have a single view somewhere decorated with #require_GET, if you want to control headers, and return an HttpResponse object with the desired payload as application/json?
The fact that your object is a singleton and all other RESTful interactions with it are prohibited suggests that a REST library is the wrong tool for this job.
I am trying to define a base request handling class so that the webapp pages may inherit some basic methods and variable which otherwise would be required to be repeatedly be defined for each page of the application. A sort of similar functionality like django preprocessors. This is my base class from which other pages inherit:
class BasePage(webapp.RequestHandler):
def __init__(self):
self.user = users.get_current_user()
self.template_values = {
'user': self.user,
'environ': self, #I don't like the idea of passing the whole environ object to a template
##The below three functions cannot be executed during _init_ because of absence of self.request
#'openid_providers': self.openid_providers(),
#'logout_url': self.get_logout_url(),
#'request': self.get_request(),
}
##A sort of similar functionality like render_to_response in django
def render_template(self, template_name, values = None, *args, **kwargs):
#PATH is the directory containing the templates
if values:
for value in values: self.template_values[value] = values[value]
self.response.out.write(template.render(PATH+template_name, self.template_values, *args, **kwargs))
##Returns request as the name suggests
def logout_url(self):
return users.create_logout_url(self.request.url)
##Returns request as the name suggests
def request(self):
return request
##Returns openid login urls
def openid_providers(self):
#OPENID_POVIDERS is a list of dictionary
for p in OPENID_PROVIDERS:
p['login_url'] = users.create_login_url(self.request.get('next', '/') , p['name'], p['url'])
return OPENID_PROVIDERS
Everything is working fine except that I cannot pass some variables during initialization as self.request is not available. So for a workaround what I did is pass on whole self variable as a template variable.
Is there some other way to provide the template variables (request, logout_url etc) to the templates?
A much simpler solution than bgporter's is to do the common setup in the initialize method of webapp.RequestHandler. Here's an example from work, where we wanted to add a Django-like is_ajax method to the request object:
class BaseHandler(webapp.RequestHandler):
def initialize(self, request, response):
super(BaseHandler, self).initialize(request, response)
# Add a Django-like is_ajax() method to the request object
request.is_ajax = lambda: \
request.environ.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
This method is called to, uh, initialize each request handler with the current request and response objects, before the appropriate get or post (or whatever) methods are called.
I've solved that problem in my AppEngine code by using the Template Method Pattern
Basically, the base class looks like:
class MyBasePage(webapp.RequestHandler):
def __init__(self):
# common setup/init stuff here,
# omitted for this discussion
def Setup(self):
# request handling setup code needed in both GET/POST methods, like
# checking for user login, getting session cookies, etc.
# omitted for this discussion
def get(self, *args):
self.Setup()
# call the derived class' 'DoGet' method that actually has
# the logic inside it
self.DoGet(*args)
def post(self, *args):
self.Setup()
# call the derived class' 'DoPost' method
self.DoPost(*args)
def DoGet(self, *args):
''' derived classes override this method and
put all of their GET logic inside. Base class does nothing.'''
pass
def DoPost(self, *args):
''' derived classes override this method and
put all of their POST logic inside. Base class does nothing.'''
pass
...your derived classes then mostly just need to worry about the guts of those DoGet() and DoPost() methods.