Example Nesting

In response to a question asked in Gitter.

Hey :slight_smile:

I’ve just stumbled onto Sanic, and I want to build class views similar pyramid eg:

@route('/user/<username>')
class UserView(ClassViewParent):
    def set_context(self, username):
        self.request.user = ORM.objects.get_or_404(username=username)

    @app.route("/", methods=["GET"])
    def show_profile(self):
        # here we have access to self.request and self.context which is the user object
        # show the user profile

    @app.route("/", methods=["PUT"])
    def update_user(self):
        # here we have access to self.request and self.context which is the user object

    @app.route("/delete", methods=["GET"])
    def delete_user_confirm(self):
        # here we have access to self.request and self.context which is the user object
        # sends user to confirm page to delete account

    @app.route("/delete", methods=["DELETE"])
    def delete_user(self):
        # here we have access to self.request and self.context which is the user object
        # deletes account

should I be creating a considering creating my own class that does this?
am I wrong to want todo things this way?
can you suggest a better way or perhaps code that is already out there that I should try?

I have created this class type view in flask before, however I remember is not being as awesome as I wanted it to be

Thanks @ahopkins, as far as I can see the ClassViews only allow you to add get, port, put, etc methods to that class, where I am looking for more of a functional view encased in a class. I’m not sure if my idea is a bad idea, perhaps I am looking for something between a mix of Class Views and Blueprints


This is (IMO) one of the places where Sanic being “unopionated” comes through. There are a few different ways this could be handled, but I will present two here: middleware and decorators.

Also, for sake of completeness, I am putting part of the example inside a Blueprint.

from sanic import Sanic, Blueprint
from sanic.views import HTTPMethodView
from sanic.response import text
from sanic.exceptions import InvalidUsage

app = Sanic("nesting")
app.config.FALLBACK_ERROR_FORMAT = "text"
bp = Blueprint("user", url_prefix="/user/<username>")


@app.middleware
async def inject_xyz(request):
    print("Execute on all requests")
    request.ctx.xyz = "XYZ"


@bp.middleware
async def inject_user_id(request):
    print("Execute on /user requests")
    request.ctx.user_id = 99


def inject_abc(fn):
    def wrapper(request):
        request.ctx.abc = "ABC"

        return fn(request)

    return wrapper


class Foo(HTTPMethodView):
    decorators = [inject_abc]

    async def get(self, request):
        return text(request.ctx.abc)

    async def post(self, request):
        return text(request.ctx.xyz)


class UserRoot(HTTPMethodView):
    async def get(self, request, username):
        return text(f"User ID for {username} is {request.ctx.user_id}")


class UserDelete(HTTPMethodView):
    async def delete(self, request, username):
        if username == "superman":
            raise InvalidUsage("You cannot defeat Superman!")
        return text("Deleted")


bp.add_route(UserRoot.as_view(), "/")
bp.add_route(UserDelete.as_view(), "/delete")
app.add_route(Foo.as_view(), "/foo")
app.blueprint(bp)
app.run(debug=True, port=9999)

Now, let’s query:

$ curl localhost:9999/foo                           
ABC
$ curl localhost:9999/foo -X POST                   
XYZ
$ curl localhost:9999/user/superman                 
User ID for superman is 99
$ curl localhost:9999/user/superman/delete -X DELETE
⚠️ 400 — Bad Request
====================
You cannot defeat Superman!

And, the server logs:

[2021-01-27 13:19:29 +0200] [2705884] [INFO] Goin' Fast @ http://127.0.0.1:9999
[2021-01-27 13:19:29 +0200] [2705884] [INFO] Starting worker [2705884]
Execute on all requests
[2021-01-27 13:20:16 +0200] - (sanic.access)[INFO][127.0.0.1:44014]: GET http://localhost:9999/foo  200 3
Execute on all requests
[2021-01-27 13:20:20 +0200] - (sanic.access)[INFO][127.0.0.1:44018]: POST http://localhost:9999/foo  200 3
Execute on all requests
Execute on /user requests
[2021-01-27 13:20:25 +0200] - (sanic.access)[INFO][127.0.0.1:44020]: GET http://localhost:9999/user/superman  200 26
Execute on all requests
Execute on /user requests
[2021-01-27 13:20:30 +0200] - (sanic.access)[INFO][127.0.0.1:44022]: DELETE http://localhost:9999/user/superman/delete  400 77