Skip to main content

full-feature

Use when the user asks to "add a new feature", "create a new page", "build CRUD for a new entity", "add a new section to the app", "implement a full feature end-to-end", or needs a step-by-step recipe for building a complete feature across Flask + SQLAlchemy layers.

Source facts

Repository
Agents-Store/cursor-plugins
Last source activity
October 3, 2026 at 23:16
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
full-feature
description
Use when the user asks to "add a new feature", "create a new page", "build CRUD for a new entity", "add a new section to the app", "implement a full feature end-to-end", or needs a step-by-step recipe for building a complete feature across Flask + SQLAlchemy layers.
# Full Feature Recipe Step-by-step guide for building a feature that spans model, migration, routes and templates in a Flask + SQLAlchemy application. The order matters: each step depends on the one before it. The details of each step live in the plugins this stack depends on; this skill keeps the order, the boundaries and the checks. `template.md` is the checklist to fill in for each new feature. Start from a project that follows `flask-dev` → `project-scaffold` (factory, `extensions.py`, a `User` model, migrations) and read `layers-and-boundaries` once: it explains the commit, the app context and the loader rules that the steps below rely on. ## Step 1: Define the Model and Migrate Add the model in `models.py` (typed 2.0 style; `OwnedMixin` gives it the `user_id` that scopes every query; models: `sqlalchemy-dev` → `model-patterns`, `references/flask-login-user.md`): ```python # models.py (continued) class NewEntity(OwnedMixin, db.Model): __tablename__ = 'new_entities' id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(String(100)) created_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=utcnow) ``` Then create, read and apply the migration. The schema reaches the database only through a revision; no table-building call belongs in the app factory (`flask-dev` → `app-patterns` says which one and why): ```bash flask db migrate -m "Add new_entities table" grep -n "create_table\|add_column" migrations/versions/*.py # the revision must contain your change flask db upgrade flask db check # models and revisions agree ``` A revision without your table means the model module is not imported by `create_app()` (see `layers-and-boundaries`, section 1). ## Step 2: Create the Blueprint Create `routes/new_entity.py`. Every query starts from `owned_by(Model, current_user.id)` (`sqlalchemy-dev` → `query-patterns`, `references/owner-scoped-queries.md`): ```python # routes/new_entity.py from flask import Blueprint, render_template from flask_login import current_user, login_required from extensions import db from models import NewEntity from queries import owned_by new_entity_bp = Blueprint('new_entity', __name__) @new_entity_bp.route('/new-entities') @login_required def list_entities(): entities = db.session.scalars( owned_by(NewEntity, current_user.id).order_by(NewEntity.name, NewEntity.id) ).all() return render_template('new_entity.html', entities=entities) ``` Register it in `create_app()`: ```python from routes.new_entity import new_entity_bp app.register_blueprint(new_entity_bp) ``` ## Step 3: Add CRUD Routes Add create, edit and delete routes in the blueprint. The patterns are in `flask-dev` → `app-patterns`, `references/crud-views.md`. The boundary rules for each of them: - `@login_required`, and a record fetched by an id from the URL is fetched together with its owner (`db.one_or_404(owned_by(...).where(...))`): another user's id is a `404`. - A foreign key the form submits (`client_id`) is fetched through `owned_by` before it is used. - Every `POST` form has `csrf_token`; delete is a `POST`. - The route ends a successful write with one `commit()` and a redirect (`layers-and-boundaries`, sections 3 and 4). ## Step 4: Create the Template Create `templates/new_entity.html` extending `base.html`: ```jinja2 {# templates/new_entity.html #} {% extends "base.html" %} {% block title %}New entities{% endblock %} {% block extra_css %} <link rel="stylesheet" href="{{ url_for('static', filename='css/new_entity.css') }}"> {% endblock %} {% block content %} {% for entity in entities %} <div class="entity-card">{{ entity.name }}</div> {% else %} <p>Nothing here yet.</p> {% endfor %} {% endblock %} ``` Add the list view with search and filter, the add form, the edit form and the delete form with confirmation as in `crud-views.md`. The template only reads what the route loaded: a relationship it touches must be named in the route's `options(...)` (`lazy='raise'` on the model turns a forgotten one into an error). ## Step 5: Add Styles Create `static/css/new_entity.css`; the template above links it in the `extra_css` block. Follow the design tokens in `base.css`. ## Step 6: Add Navigation Add a nav link in `base.html`: ```jinja2 <a href="{{ url_for('new_entity.list_entities') }}" class="{% if request.blueprint == 'new_entity' %}active{% endif %}"> New Entity </a> ``` ## Step 7: Verify 1. `flask routes` shows every endpoint of the blueprint; `flask run` and click through Create, Read, Update, Delete. 2. `pytest` (`pytest.ini` turns SQLAlchemy deprecation warnings into errors) with these tests for the feature: - CRUD round trip through the test client. - A second user gets `404` for the first user's id on edit and delete (`owner-scoped-queries.md`, Test the Isolation). - The list view issues the same number of statements for 2 rows and for 22 (`layers-and-boundaries`, section 5). - Every write is committed: assert in a fresh `with app.app_context():` after the request (`layers-and-boundaries`, section 7). 3. Validation: required fields, duplicates, unknown ids. 4. `flask db check` is clean. ## Checklist - [ ] Model defined with `OwnedMixin` (a `user_id` foreign key) - [ ] Migration created, read, applied; `flask db check` clean - [ ] Blueprint created and registered - [ ] CRUD routes with `@login_required` - [ ] All queries start from `owned_by(Model, current_user.id)`; foreign keys from forms fetched the same way - [ ] Template extends `base.html`; no query or unloaded relationship in it - [ ] Every `POST` form carries `csrf_token`; delete is a `POST` - [ ] Flash messages for success and error - [ ] CSS file created and linked - [ ] Nav link added to `base.html` - [ ] Tests: CRUD, two-user isolation, statement count, commit
View on GitHub