Advanced Demo - flarchitect
Description: This annotated example combines soft deletes, nested writes and custom callbacks. The code lives in demo/advanced_features/app.py. Key points: Soft deletes are enabled via API_SOFT_DELETE and the d...

Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
View this page
Toggle table of contents sidebar

# 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