Advanced Demo

This annotated example combines soft deletes, nested writes and custom callbacks. The code lives in demo/advanced_features/app.py.

  1from __future__ import annotations
  2
  3import datetime
  4from typing import Any
  5
  6from flask import Flask
  7from flask_sqlalchemy import SQLAlchemy
  8from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String
  9from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
 10
 11from flarchitect import Architect
 12
 13
 14class BaseModel(DeclarativeBase):
 15    """Base model with timestamp and soft delete columns."""
 16
 17    created: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime.utcnow)
 18    updated: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
 19    deleted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
 20
 21    def get_session(*args: Any, **kwargs: Any):
 22        """Return the current database session."""
 23
 24        return db.session
 25
 26
 27db = SQLAlchemy(model_class=BaseModel)
 28
 29
 30class Author(db.Model):
 31    """Author of one or more books."""
 32
 33    __tablename__ = "author"
 34
 35    class Meta:
 36        tag = "Author"
 37        tag_group = "People"
 38
 39    id: Mapped[int] = mapped_column(Integer, primary_key=True)
 40    name: Mapped[str] = mapped_column(String(80))
 41    books: Mapped[list[Book]] = relationship(back_populates="author")
 42
 43
 44class Book(db.Model):
 45    """Book written by an author."""
 46
 47    __tablename__ = "book"
 48
 49    class Meta:
 50        tag = "Book"
 51        tag_group = "Content"
 52        allow_nested_writes = True
 53        add_callback = staticmethod(lambda obj, model: _add_callback(obj))
 54
 55    id: Mapped[int] = mapped_column(Integer, primary_key=True)
 56    title: Mapped[str] = mapped_column(String(120))
 57    author_id: Mapped[int] = mapped_column(ForeignKey("author.id"))
 58    author: Mapped[Author] = relationship(back_populates="books")
 59
 60
 61def _add_callback(obj: Book) -> Book:
 62    """Ensure book titles are capitalised before saving."""
 63
 64    obj.title = obj.title.title()
 65    return obj
 66
 67
 68def return_callback(model: type[BaseModel], output: dict[str, Any], **kwargs: Any) -> dict[str, Any]:
 69    """Attach a debug flag to every response.
 70
 71    Args:
 72        model: Model class being processed.
 73        output: Response payload.
 74
 75    Returns:
 76        Modified response dictionary.
 77    """
 78
 79    output["debug"] = True
 80    return {"output": output}
 81
 82
 83def create_app() -> Flask:
 84    """Build the Flask application and initialise flarchitect.
 85
 86    Returns:
 87        Configured Flask application.
 88    """
 89
 90    app = Flask(__name__)
 91    app.config.update(
 92        SQLALCHEMY_DATABASE_URI="sqlite:///:memory:",
 93        API_TITLE="Advanced API",
 94        API_VERSION="1.0",
 95        API_BASE_MODEL=db.Model,
 96        API_ALLOW_NESTED_WRITES=True,
 97        API_SOFT_DELETE=True,
 98        API_SOFT_DELETE_ATTRIBUTE="deleted",
 99        API_SOFT_DELETE_VALUES=(False, True),
100        API_RETURN_CALLBACK=return_callback,
101    )
102
103    db.init_app(app)
104    with app.app_context():
105        db.create_all()
106        Architect(app)
107
108    return app
109
110
111if __name__ == "__main__":
112    create_app().run(debug=True)

Key points

  • Soft deletes are enabled via API_SOFT_DELETE and the deleted column on BaseModel.

  • Nested writes allow creating related objects in one request. Book.Meta.allow_nested_writes turns it on for books.

  • Custom callbacks modify behaviour: return_callback injects a debug flag into every response and Book.Meta.add_callback title-cases book names before saving.

Run the demo

python demo/advanced_features/app.py
curl -X POST http://localhost:5000/api/book \
     -H "Content-Type: application/json" \
     -d '{"title": "my book", "author": {"name": "Alice"}}'
curl http://localhost:5000/api/book?include_deleted=true