Class-based views and JSON APIs

Flask · Advanced · 12 min

REST resources repeat the same GET/POST/PATCH/DELETE structure, which classes express compactly.

Function views are Flask's default, but for REST-style resources a `MethodView` maps each HTTP method to a method of the class:

class ItemAPI(MethodView):
    def get(self, item_id): ...
    def patch(self, item_id): ...
    def delete(self, item_id): ...

Register it with app.add_url_rule("/items/<int:item_id>", view_func=ItemAPI.as_view("item")). Flask reads the methods the class defines and sets the allowed HTTP methods automatically; others get 405.

A common pattern uses two URLs for one resource type: a collection (/items/ with get to list and post to create) and an item (/items/<id> with get, patch, delete). A small helper function can register both for any model, which is where classes shine: subclass, set model = User, and reuse everything.

Details:

  • as_view(name, *args) passes arguments to __init__; set init_every_request = False to create the instance once instead of per request (only safe if it holds no per-request state).
  • decorators = [login_required] as a class attribute applies decorators to the generated view function.
  • The plain View base class with a dispatch_request method is the lower-level alternative.

For larger APIs, extensions such as Flask-Smorest or APIFlask add request validation, serialization and OpenAPI docs on top of this pattern. If an API is your main goal and you are starting fresh, also consider FastAPI.

Example

from flask import Flask, request
from flask.views import MethodView

app = Flask(__name__)
ITEMS = {1: {"name": "pen"}}

class ItemCollection(MethodView):
    def get(self):
        return [{"id": i, **v} for i, v in ITEMS.items()]
    def post(self):
        new_id = max(ITEMS, default=0) + 1
        ITEMS[new_id] = {"name": request.get_json()["name"]}
        return {"id": new_id}, 201
class Item(MethodView):
    def get(self, item_id):
        return ITEMS.get(item_id) or ({"error": "not found"}, 404)
    def delete(self, item_id):
        ITEMS.pop(item_id, None)
        return "", 204
app.add_url_rule("/items/", view_func=ItemCollection.as_view("items"))
app.add_url_rule("/items/<int:item_id>", view_func=Item.as_view("item"))
c = app.test_client()
print(c.post("/items/", json={"name": "ink"}).json, c.get("/items/").json)
# {'id': 2} [{'id': 1, 'name': 'pen'}, {'id': 2, 'name': 'ink'}]
print(c.delete("/items/1").status_code, c.put("/items/1").status_code)   # 204 405

Remember

  • MethodView maps HTTP methods to class methods; allowed methods are inferred
  • Register with add_url_rule(path, view_func=Cls.as_view("name"))
  • Collection and item views together form a REST resource

Watch out for

  • Storing per-request data on self with init_every_request = False, which shares it across requests.

Check yourself

  1. A MethodView defines only get and delete. What does a PUT to its URL return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/views/