I'm using flask restful to create an API and I want to refactor my code. I have something as shown below:
class Makequestions(Resource):
def get(self):
#somecode
class Managequestions(Resource):
def get(self, user_id):
#somecode
API.add_resource(MakeQuestions, '/MakeQuestions')
API.add_resource(ManageQuestions, '/ManageQuestions/<int:user_id>')
I need to combine these two into a single resource with multiple endpoints say forexample:
class Makequestions(Resource):
def get(self):
#somecode
def get(self, user_id):
#somecode
maybe with a final url along the lines of
API.add_resource(MakeQuestions, '/MakeQuestions','/MakeQuestions/<int:user_id>')
I'm hoping this will reduce the amount of times I have to create a 'Resource class' Is there someway I can do this?
I'm not super familiar with the Flask RESTful API, but while Python will let you overload a class with get(self) and get(self, question_id), if you're "making" a REST resource, that shouldn't be a GET request.
REST API URLs should not contain verbs such as "MakeObject" or "ManageObject". The HTTP method should dictate the operation being performed.
That being said, POST /question would make a question object. GET /question/:id should return a specific one.
Therefore, you'd define a post(self) and get(self, question_id) under a single Question Resource. Or add a Questions resource as well where you can get all questions, and move the post method there.
If you replace "Question" with "TODO", your problem is not that different from the full example on the Flask RESTful site
Note: plural URLs are also preferred over singular, so in your case, /questions and /questions/:id should be defined as two resources
The naming of your functions aside, you could probably do something like
class Question(Resource):
def get(self, user_id=0):
if(user_id):
return specific question
return list of questions
Related
I have two endpoints:
/api/v1/users
/api/v1/<resource>/users
I have a class that handles for the latter like this
class ListUsers(Resource):
def post(self,resource):
// get the list of users under that resource
api.addResource(ListUsers,'/api/v1/<resource>/users')
If the resource is not specified, I want to list users from all resources. Is it possible to map both the urls to the same class ListUsers instead of writing another class for the first url?
According the the docs and source code for flask-restful, you can pass multiple urls to match to addResource.
like:
class ListUsers(Resource):
def post(self, resource=None):
// get the list of users under that resource
api.addResource(ListUsers, '/api/v1/<resource>/users', '/api/v1/users')
Another example: https://stackoverflow.com/a/56250131/1788218
Useful docs: https://buildmedia.readthedocs.org/media/pdf/flask-restful/latest/flask-restful.pdf
Consider simple view:
class SomeView(viewsets.GenericViewSet,
viewsets.mixins.ListModelMixin,
viewsets.mixins.RetrieveModelMixin):
...
#decorators.detail_route(methods=ENDPOINT_PROPERTY_METHODS)
def some_property(self, request, *args, **kwargs):
view = SomeOtherView
return view.as_view(CRUD_ACTIONS)(request, *args, **kwargs)
I'm calling SomeOtherView to have ability to have an endpoint-property like /someresource/:id/myproperty, so this property will receive request and can do all CRUD actions.
But, I want to SomeOtherView to have the declared detail_route inside too to have something like /someresource/:id/myproperty/nestedproperty.
Since I'm calling SomeOtherView dynamically, urls can not be registered, so nested property can not be called.
How I can resolve such situation to have nested properties?
There is currently no native way in automatically creating nested routes in django-rest-framework but there are some ways to achieve your goal:
use drf-extentions, what your are searching for are nested routers: https://chibisov.github.io/drf-extensions/docs/#nested-routes
create the paths manually with the default routers, here you need to filter your queryset manually
Although you didn't explain in detail what you want to achive with this api structure I wouldn't recomment continuing this path because views are not intended to be used like that.
I want to make the regex capture group in a url optional. i.e. I want to handle both example.com/url and also example.com/url/something by a single handler.
I can use (r'/0(/?\w*)', Handler), but is that the right way to do it? If I go by this way, then I need to have a if-else block to see whether the requested url is /0 or /0/something.
Ideally, I would like something like this. (r'/0', r'/0/(\w+)' Handler). A handler which can accept two different url types and work on it. And in handler implementation, I would see if the parameter input to the handler is None, then I would display /0 page and consider user has submitted an answer otherwise.
Here is the reason why I want to do this. I am building a simple quiz app, where question will rendered by example.com/0 url and user will submit his answer as example.com/0/answer. I have 100 questions. In that case if I am handling questions and answers separately, then I would have 200 handlers against 100 handlers.
Or is this a bad design overall? Instead of creating 100 handlers, I have to use if-else blocks. Any suggestions?
PS: I don't want to change url formats. i.e. questions always will be displayed on example.com/0 (and not example.com/0/) and user will submit his answers as example.com/0/answer
I would just use HTTP methods and a single handler:
class QuestionHandler(RequestHandler):
def get(self, id):
# Display the question
def post(self, id):
# Validates the answer
answer = self.get_argument('answer')
application = Application([
(r'/(\d+)', QuestionHandler),
])
But if you insist on using a separate handlers:
class QuestionHandler(RequestHandler):
def get(self, id):
# Display the question
class AnswerHandler(RequestHandler):
def post(self, id):
# Validates the answer
answer = self.get_argument('answer')
application = Application([
(r'/(\d+)', QuestionHandler),
(r'/(\d+)/answer', AnswerHandler),
])
I've been reading on the ways to implement authorization (and authentication) to my newly created Pyramid application. I keep bumping into the concept called "Resource". I am using python-couchdb in my application and not using RDBMS at all, hence no SQLAlchemy. If I create a Product object like so:
class Product(mapping.Document):
item = mapping.TextField()
name = mapping.TextField()
sizes = mapping.ListField()
Can someone please tell me if this is also called the resource? I've been reading the entire documentation of Pyramids, but no where does it explain the term resource in plain simple english (maybe I'm just stupid). If this is the resource, does this mean I just stick my ACL stuff in here like so:
class Product(mapping.Document):
__acl__ = [(Allow, AUTHENTICATED, 'view')]
item = mapping.TextField()
name = mapping.TextField()
sizes = mapping.ListField()
def __getitem__(self, key):
return <something>
If I were to also use Traversal, does this mean I add the getitem function in my python-couchdb Product class/resource?
Sorry, it's just really confusing with all the new terms (I came from Pylons 0.9.7).
Thanks in advance.
I think the piece you are missing is the traversal part. Is Product
the resource? Well it depends on what your traversal produces, it
could produce products.....
Perhaps it might be best to walk this through from the view back to
how it gets configured when the application is created...
Here's a typical view.
#view_config(context=Product, permission="view")
def view_product(context, request):
pass # would do stuff
So this view gets called when context is an instance of Product. AND
if the acl attribute of that instance has the "view"
permission. So how would an instance of Product become context?
This is where the magic of traversal comes in. The very logic of
traversal is simply a dictionary of dictionaries. So one way that this
could work for you is if you had a url like
/product/1
Somehow, some resource needs to be traversed by the segments of the
url to determine a context so that a view can be determined. What if
we had something like...
class ProductContainer(object):
"""
container = ProductContainer()
container[1]
>>> <Product(1)>
"""
def __init__(self, request, name="product", parent=None):
self.__name__ = name
self.__parent__ = parent
self._request = request
def __getitem__(self, key):
p = db.get_product(id=key)
if not p:
raise KeyError(key)
else:
p.__acl__ = [(Allow, Everyone,"view")]
p.__name__ = key
p.__parent__ = self
return p
Now this is covered in the documentation and I'm attempting to boil it
down to the basics you need to know. The ProductContainer is an object
that behaves like a dictionary. The "name" and "parent"
attributes are required by pyramid in order for the url generation
methods to work right.
So now we have a resource that can be traversed. How do we tell
pyramid to traverse ProductContainer? We do that through the
Configurator object.
config = Configurator()
config.add_route(name="product",
path="/product/*traverse",
factory=ProductContainer)
config.scan()
application = config.make_wsgi_app()
The factory parameter expects a callable and it hands it the current
request. It just so happens that ProductContainer.init will do
that just fine.
This might seem a little much for such a simple example, but hopefully
you can imagine the possibilities. This pattern allows for very
granular permission models.
If you don't want/need a very granular permission model such as row
level acl's you probably don't need traversal, instead you can use
routes with a single root factory.
class RootFactory(object):
def __init__(self, request):
self._request = request
self.__acl__ = [(Allow, Everyone, "view")] # todo: add more acls
#view_config(permission="view", route_name="orders")
def view_product(context, request):
order_id, product_id = request.matchdict["order_id"], request.matchdict["product_id"]
pass # do what you need to with the input, the security check already happened
config = Configurator(root_factory=RootFactory)
config.add_route(name="orders",
path="/order/{order_id}/products/{product_id}")
config.scan()
application = config.make_wsgi_app()
note: I did the code example from memory, obviously you need all the necessary imports etc. in other words this isn't going to work as a copy/paste
Have you worked through http://michael.merickel.org/projects/pyramid_auth_demo/ ? If not, I suspect it may help. The last section http://michael.merickel.org/projects/pyramid_auth_demo/object_security.html implements the pattern you're after (note the example "model" classes inherit from nothing more complex than object).
I'm making a server that can let clients upload and download data of different models. Is there some elegant way handle the requests?
More precisely, I don't want to do something like this,
app = webapp.WSGIApplication([
('/my_upload_and_download_url/ModelA/(.*)', MyRequestHandlerForA),
('/my_upload_and_download_url/ModelB/(.*)', MyRequestHandlerForB),
('/my_upload_and_download_url/ModelC/(.*)', MyRequestHandlerForC),
])
run_wsgi_app(app)
since what I do inside the handler would all be the same. For example,
class MyRequestHandlerForX(webapp.RequestHandler):
def get(self, key=None):
# return the instance with the designated key
def post(self, key=None):
# create/get the model instance
# iterate through the property list of the instance and set the values
the only difference among the handlers is to create instance for different models. The urls are alike, and the handlers are almost the same.
I checked this post about redirect requests to other handlers, and I've also read some methods to create an instance by a class name; but I think neither of them is good.
Anyone has a good solution?
p.s. This is my first post here. If there is anything inappropriate please tell me, thanks.
How you do this depends largely on the details of your code in the request handler. You can do a fairly generic one like this:
class ModelHandler(webapp.RequestHandler):
def get(self, kind, key):
model = db.class_for_kind(kind)
instance = model.get(key)
# Do something with the instance - eg, print it out
def post(self, kind, key):
model = db.class_for_kind(kind)
instance = model.create_from_request(self.request)
application = webapp.WSGIApplication([
('/foo/([^/]+)/([^/]+)', ModelHandler),
])
def main():
run_wsgi_app(application)
if __name__ == '__main__':
main()
This assumes you define a 'create_from_request' class method on each model class; you probably don't want to do it exactly this way, as it tightly couples model definitions with the forms used to input them; instead, you probably want to store a mapping of kind name or class to handler function, or do your forms and creation entirely automatically by reflecting on the properties of the class. Since you haven't specified what it is about doing this you're unsure about, it's hard to be more specific.
Also note the inclusion of a main() and other boilerplate above; while it will work the way you've pasted it, adding a main is substantially more efficient, as it allows the App Engine runtime to avoid having to evaluate your module on every request.
In your case I'd probably just have everything hit the same url path, and put the specifics in the GET parameters, like /my_upload_and_download_url?model=modelA.
You can also use webapp2 (http://webapp-improved.appspot.com/guide/app.html) which has a bunch of url routing support.
You could parse out the url path and do a look up, like this:
import urlparse
model_lookup = {'ModelA':ModelA,'ModelB':ModelB, 'ModelC':ModelC}
class MyRequestHandler(webapp.RequestHandler):
def get(self):
url = urlparse.urlparse(self.request.uri)
path_model = url.path.replace('/my_upload_and_download_url/','')
model = model_lookup[path_model]
...
Which allows you to use the same class for each path:
app = webapp.WSGIApplication([
('/my_upload_and_download_url/ModelA/(.*)', MyRequestHandler),
('/my_upload_and_download_url/ModelB/(.*)', MyRequestHandler),
('/my_upload_and_download_url/ModelC/(.*)', MyRequestHandler),
])
run_wsgi_app(app)