Skip to content

Base Views

The BaseView class provides a foundation for creating class-based views in Wiverno. It automatically dispatches requests to methods based on HTTP method.

Module: wiverno.views.base_views

Overview

Class-based views are an alternative to function-based views. They allow you to organize view logic in classes with methods for different HTTP methods:

  • GET - get(request)
  • POST - post(request)
  • PUT - put(request)
  • DELETE - delete(request)
  • PATCH - patch(request)
  • Other HTTP methods - connect(request), head(request), options(request), trace(request)

Basic Usage

from wiverno.main import Wiverno
from wiverno.views.base_views import BaseView
from wiverno.core.requests import Request

class ItemsView(BaseView):
    def get(self, request: Request) -> tuple[str, str]:
        return "200 OK", "List of items"

    def post(self, request: Request) -> tuple[str, str]:
        return "201 CREATED", "Item created"

app = Wiverno()
app.route("/items")(ItemsView())

Class Definition

BaseView

Base class for class-based views.

from wiverno.views.base_views import BaseView

class MyView(BaseView):
    """Define methods for each HTTP method you want to handle."""

    def get(self, request):
        return "200 OK", "GET response"

    def post(self, request):
        return "201 CREATED", "POST response"

Methods

HTTP Method Handlers

Define methods named after HTTP methods (lowercase) to handle requests:

get(request) -> tuple[str, str]

Handle GET requests.

class UserListView(BaseView):
    def get(self, request):
        return "200 OK", "Users list"

post(request) -> tuple[str, str]

Handle POST requests.

class UserListView(BaseView):
    def post(self, request):
        # request.data contains POST body
        name = request.data.get('name')
        return "201 CREATED", f"User {name} created"

put(request) -> tuple[str, str]

Handle PUT requests (full resource replacement).

class UserDetailView(BaseView):
    def put(self, request):
        # request.data contains new resource data
        return "200 OK", "User updated"

patch(request) -> tuple[str, str]

Handle PATCH requests (partial resource update).

class UserDetailView(BaseView):
    def patch(self, request):
        # request.data contains partial update
        return "200 OK", "User partially updated"

delete(request) -> tuple[str, str]

Handle DELETE requests.

class UserDetailView(BaseView):
    def delete(self, request):
        return "204 NO CONTENT", ""

connect(request), head(request), options(request), trace(request)

Handle other HTTP methods.

class ResourceView(BaseView):
    def head(self, request):
        return "200 OK", ""

    def options(self, request):
        return "200 OK", "GET,POST,PUT,DELETE"

__call__(request) -> tuple[str, str]

The magic method that makes the view callable. Automatically dispatches to the appropriate HTTP method handler.

Parameters:

  • request (Request): The incoming request object

Returns: tuple[str, str] - (status_string, html_body)

Raises:

  • Returns 405 Method Not Allowed if the method handler is not implemented

This method is called automatically by Wiverno and typically doesn't need to be called directly.

view = MyView()
status, body = view(request)  # Calls get/post/etc based on request.method

Request Dispatching

BaseView automatically routes requests to methods based on request.method:

class APIView(BaseView):
    def get(self, request):
        return "200 OK", "GET response"

    def post(self, request):
        return "201 CREATED", "POST response"

# When request.method == "GET", get() is called
# When request.method == "POST", post() is called
# When request.method == "PUT", 405 is returned (not implemented)

Error Handling

If the HTTP method is not implemented, BaseView automatically returns a 405 Method Not Allowed response:

class GetOnlyView(BaseView):
    def get(self, request):
        return "200 OK", "Data"

    # PUT, POST, DELETE not implemented
    # POST request will return 405

view = GetOnlyView()
request.method = "POST"
status, body = view(request)
# status == "405 METHOD NOT ALLOWED"

Registration with Application

Using Decorator

Register a class-based view using the @app.route() or HTTP method decorators:

from wiverno.main import Wiverno
from wiverno.views.base_views import BaseView

app = Wiverno()

class UserView(BaseView):
    def get(self, request):
        return "200 OK", "Users"

    def post(self, request):
        return "201 CREATED", "User created"

# Register the instantiated view
app.route("/users")(UserView())

With Router

Use class-based views with routers:

from wiverno.core.routing.router import Router

router = Router()

class ItemView(BaseView):
    def get(self, request):
        return "200 OK", "Items"

router.route("/items")(ItemView())

app.include_router(router, prefix="/api")

Usage Examples

Simple GET and POST Handler

from wiverno.views.base_views import BaseView
from wiverno.core.requests import Request

class ContactView(BaseView):
    def get(self, request: Request) -> tuple[str, str]:
        return "200 OK", """
            <form method="POST">
                <input type="text" name="message">
                <button>Send</button>
            </form>
        """

    def post(self, request: Request) -> tuple[str, str]:
        message = request.data.get('message')
        return "200 OK", f"Message received: {message}"

# Register
app.route("/contact")(ContactView())

REST API Endpoint

class ArticleView(BaseView):
    def get(self, request: Request) -> tuple[str, str]:
        # Get all articles
        return "200 OK", '[{"id": 1, "title": "First"}]'

    def post(self, request: Request) -> tuple[str, str]:
        # Create new article
        data = request.data
        title = data.get('title')
        return "201 CREATED", f'{{"title": "{title}"}}'

    def put(self, request: Request) -> tuple[str, str]:
        # Update article
        return "200 OK", '{"updated": true}'

    def delete(self, request: Request) -> tuple[str, str]:
        # Delete article
        return "204 NO CONTENT", ""

app.route("/articles")(ArticleView())

Combining with Templates

from wiverno.templating.templator import Templator

class ProfileView(BaseView):
    def __init__(self):
        self.templator = Templator()

    def get(self, request: Request) -> tuple[str, str]:
        user_id = request.query_params.get('id')
        html = self.templator.render("profile.html", {
            "user_id": user_id
        })
        return "200 OK", html

    def post(self, request: Request) -> tuple[str, str]:
        # Update profile
        return "200 OK", "Profile updated"

app.route("/profile")(ProfileView())

API Validation

import json

class DataAPIView(BaseView):
    def get(self, request: Request) -> tuple[str, str]:
        return "200 OK", json.dumps({"status": "ok"})

    def post(self, request: Request) -> tuple[str, str]:
        # Validate request data
        required_fields = {'name', 'email'}
        provided_fields = set(request.data.keys())

        if not required_fields.issubset(provided_fields):
            return "400 BAD REQUEST", "Missing required fields"

        # Process data
        return "201 CREATED", json.dumps({"created": True})

    def put(self, request: Request) -> tuple[str, str]:
        if not request.data:
            return "400 BAD REQUEST", "Empty body"

        return "200 OK", json.dumps({"updated": True})

app.route("/api/data")(DataAPIView())

Using Class State

class CacheView(BaseView):
    def __init__(self):
        self.cache = {}

    def get(self, request: Request) -> tuple[str, str]:
        key = request.query_params.get('key')
        if key in self.cache:
            return "200 OK", self.cache[key]
        return "404 NOT FOUND", "Not found"

    def post(self, request: Request) -> tuple[str, str]:
        key = request.data.get('key')
        value = request.data.get('value')
        self.cache[key] = value
        return "201 CREATED", "Cached"

cache_view = CacheView()
app.route("/cache")(cache_view)

Multi-Action View

class AdminDashboardView(BaseView):
    def get(self, request: Request) -> tuple[str, str]:
        action = request.query_params.get('action', 'overview')

        if action == 'overview':
            return "200 OK", "Dashboard overview"
        elif action == 'users':
            return "200 OK", "User management"
        elif action == 'settings':
            return "200 OK", "Settings"
        else:
            return "400 BAD REQUEST", "Invalid action"

    def post(self, request: Request) -> tuple[str, str]:
        action = request.data.get('action')

        if action == 'save_settings':
            return "200 OK", "Settings saved"
        elif action == 'create_user':
            return "201 CREATED", "User created"
        else:
            return "400 BAD REQUEST", "Invalid action"

app.route("/admin")(AdminDashboardView())

Comparison: Function-Based vs Class-Based

Function-Based View

@app.route("/items", methods=["GET", "POST"])
def items_view(request):
    if request.method == "GET":
        return "200 OK", "Items list"
    elif request.method == "POST":
        return "201 CREATED", "Item created"

Class-Based View

class ItemsView(BaseView):
    def get(self, request):
        return "200 OK", "Items list"

    def post(self, request):
        return "201 CREATED", "Item created"

app.route("/items")(ItemsView())

Class-based views are cleaner when handling multiple HTTP methods for the same resource.

See Also