name: stk-blueprint description: | Scaffold a new stk blueprint with models, views, templates, and Alembic migration. Use when creating a new feature module, adding a new section to the app, or scaffolding a blueprint. argument-hint: "[blueprint-name]"
Scaffold stk Blueprint
Create a complete blueprint for $ARGUMENTS in the stk framework.
Workflow
The scaffolder generates the full module structure. Run it first, then customize domain fields and logic.
uv run quart new <name>
<name> must be lowercase snake_case (e.g. blog_post, invoice_line). Reserved names (user, role, portal, public, session, admin, etc.) are rejected with a clear error.
The command generates:
stk/<name>/__init__.py,models.py,views.pystk/templates/cms/<name>.html- Patches
stk/app.py(import +register_blueprint) - Patches
stk/static/js/navigation.js(nav entry)
Then:
- Customize
stk/<name>/models.py-- add/rename fields to fit your domain - Run:
uv run quart db revision -m "add <name>"and review the generated migration - Apply:
uv run quart db upgrade - Verify:
uv run quart verify && uv run quart smoke
Customization reference
After scaffolding, the generated files follow these conventions. Only touch these when you need to go beyond the defaults.
Models (stk/<name>/models.py)
import dataclassesand use@dataclasses.dataclassdecorator- Import
Basefromstk.extensions, NOTdb - Use
Column(Type)from sqlalchemy directly - All relationships use
lazy="selectin" - Include
to_dict()instance method - Include
async from_dict(self, data)as instance method that mutates self (NOT a classmethod) - Include
created_at = Column(DateTime, default=datetime.now, nullable=False)
Views (stk/<name>/views.py)
- ALL handlers are
async def - Use
import orjson as jsonand returnResponse(json.dumps(data), content_type="application/json")for list endpoints - Blueprint-level auth via
@bp.before_requestwith@auth_required("session")+@roles_required("admin") - DB access via
await g.db_session.execute(...)orawait g.db_session.get(...) - Frontend sends
{item: {...}}, extract withjson_data.get("item", {}) - Wrap mutations in try/except with
await g.db_session.rollback()on error - Log mutations:
await Activity.register(current_user.id, "Action", data) - Return
{"message": "..."}for create/update/delete responses - Use
PER_PAGE = 25constant - Use
log = logging.getLogger(__name__)for error logging
Template (stk/templates/cms/<name>.html)
- Extend
layout.html - Vue 3 Options API:
data(),methods,mounted(), NOTsetup() - Must include
mixins: [layoutMixin]anddelimiters: config.delimiters - Use
const vuetify = createVuetify(config.vuetifyConfig) - Call
registerStkComponents(app)beforeapp.use(vuetify).mount("#app") - Use
v-data-table-serverfor lists - Icons: Tabler Icons (
ti ti-*), e.g.ti ti-plus,ti ti-pencil,ti ti-trash,ti ti-x - Pass server data via
<script type="application/json" id="...">{{ data|tojson|safe }}</script> - Use
toRaw()from Vue when editing items:const {createApp, toRaw} = Vue
Do NOT:
- Use
db.Model,db.Column,db.select(), or any Flask-SQLAlchemy patterns - Use
from flask import ...orfrom flask_security import ... - Use sync handlers (every route must be
async def) - Use
jsonify()(return dicts or Response objects) - Use
lazy="dynamic"on relationships (breaks async) - Forget
awaiton DB operations - Use Vue Composition API (
setup(),ref(),reactive()) -- use Options API - Use Material Design Icons -- use Tabler Icons (
ti ti-*) - Send raw data in POST -- wrap in
{item: {...}} - Forget
mixins: [layoutMixin]orregisterStkComponents(app)in templates