Juha Untinen
Juha Untinen

Reputation: 1867

Subroutes in Flask-Classy / Flask-Classful

The below works in Flask-Classful (it's a maintained fork of the now-abandoned Flask-Classy), but I was wondering if there is a "native" version of the logic I have for the last two routes in Flask-Classful, without mixing Flask routes and Flask-Classful built-in special methods like get() and post()?

From the extensive documentation Flask-Classy has at https://pythonhosted.org/Flask-Classy/ I unfortunately couldn't spot an example for the last two routes in the below code, which would be eg. GET /news/123/comments/ and GET /news/123/comments/321. There were some examples for cases that would be like GET /news/comments/123, but not for when you have two variables you pick from the route URI (which will be used for DB queries).

from flask_classful import FlaskView, route


class NewsView(FlaskView):
    def index(self):
        return "This is GET /news\n"

    def get(self, news_id):
        return "This is GET /news/{}\n".format(news_id)

    def post(self):
        return "This is POST /news\n"

    def put(self, news_id):
        return "This is PUT /news/{}\n".format(news_id)

    def patch(self, news_id):
        return "This is PATCH /news/{}\n".format(news_id)

    def delete(self, news_id):
        return "This is DELETE /news/{}\n".format(news_id)

    @route("/<int:news_id>/comments/<int:comment_id>", methods=["GET"])
    def news_comment(self, news_id, comment_id):
        return "This is GET /news/{}/comments/{}\n".format(news_id, comment_id)

    @route("/<int:news_id>/comments/", methods=["GET"])
    def news_comments(self, news_id):
        return "This is GET /news/{}/comments/\n".format(news_id)

The routes are registered with:

def register_views(app):
    api_path = "/api/1.0"

    from apps.news.views import NewsView
    NewsView.register(app, route_base="{}/news/".format(api_path))

It seems to work though. Maybe it's just splitting hairs, but I would think something like this would have built-in support in Flask-Classful?

Here's some test requests, where it works:

$ curl -X GET https://localhost:443/api/1.0/news/ --insecure -L
This is GET /news
$ curl -X GET https://localhost:443/api/1.0/news/123 --insecure -L
This is GET /news/123
$ curl -X POST https://localhost:443/api/1.0/news/ --insecure -L
This is POST /news
$ curl -X PUT https://localhost:443/api/1.0/news/123 --insecure -L
This is PUT /news/123
$ curl -X PATCH https://localhost:443/api/1.0/news/123 --insecure -L
This is PATCH /news/123
$ curl -X DELETE https://localhost:443/api/1.0/news/123 --insecure -L
This is DELETE /news/123
$ curl -X GET https://localhost:443/api/1.0/news/1/comments/ --insecure -L
This is GET /news/1/comments/
$ curl -X GET https://localhost:443/api/1.0/news/1/comments/2 --insecure -L
This is GET /news/1/comments/2

Upvotes: 2

Views: 3410

Answers (1)

Tarun Lalwani
Tarun Lalwani

Reputation: 146510

By default when you create a method using some name

class NewsView(FlaskView):
    def comments(self, news_id, comment_id):
        pass

The above by default will register it as /comments/<news_id>/<comment_id> and since your base route is news/. The final route to the comments method would become news/comments/<news_id>/<comment_id>.

But then who stops us from changing that?

from flask import Flask, request, jsonify
import json

from flask_classy import FlaskView

app = Flask(__name__)


class MyFlaskView(FlaskView):
    @classmethod
    def build_rule(cls, rule, method=None):
        path = super(MyFlaskView, cls).build_rule(rule, method)
        parts = path.split("/")
        if len(parts) >= 3 and parts[-1].startswith("<") and parts[-2].startswith("<"):
            parts[-3], parts[-2] = parts[-2], parts[-3]
            return "/".join(parts)
        return path

class NewsView(MyFlaskView):

    def comments(self, news_id, comment_id):
        return "This is GET /news\n"

    def get(self, news_id):
        return "This is GET /news2\n"

if __name__ == "__main__":
   NewsView.register(app, route_base="news/")
   app.run(debug=True)

This overridden method will register your route as /news/<news_id>/comments/<comment_id>

Upvotes: 4

Related Questions