Example of a Simple Flask CRUD App

A simple Flask / SQLite / Jinja app with routes to: Create, Read, Update, Delete data.

App Configuration

DB Schema

CREATE TABLE notes (
    id      INTEGER PRIMARY KEY AUTOINCREMENT,
    title   TEXT NOT NULL,
    body    TEXT,
    pinned  INTEGER DEFAULT 0,
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)

App Routes

Method Route Description
GET / list all notes (home)
GET /note/<int:id> show a single note
GET /note/new new note form
POST /note add a new note
GET /note/<int:id>/edit not edit form
POST /note/<int:id> update a note
GET /note/<int:id>/delete delete a note

App Templates

Name Data Needed Description
note_list.jinja notes[] Show a list of all notes
note_single.jinja note Show a single note in detail
note_form.jinja   Show a new note form
note_form_edit.jinja note Show a note edit form

Routes and Handlers

Reading Notes

# Show all notes -------------------------------------------
@app.get("/")
def show_notes():
    with connect_db() as db:
        sql = """
            SELECT id, title, body, pinned, created
            FROM notes
            ORDER BY pinned DESC, created DESC
        """
        params = ()
        notes = db.execute(sql, params).fetchall()

        return render_template("pages/note_list.jinja", notes=notes)


# Show a single note ----------------------------------------
@app.get("/note/<int:id>")
def show_a_note(id):
    with connect_db() as db:
        sql = """
            SELECT id, title, body, pinned, created
            FROM notes
            WHERE id=?
        """
        params = (id,)
        note = db.execute(sql, params).fetchone()

        return render_template("pages/note_single.jinja", note=note)

Creating Notes

# Show new note form ----------------------------------------
@app.get("/note/new")
def show_note_form():
    return render_template("pages/note_form.jinja")


# Process new note ------------------------------------------
@app.post("/note")
def add_a_note():
    title = request.form.get('title', '').strip()
    body = request.form.get('body', '').strip()

    pinned = bool(request.form.get('pin'))

    title = html.escape(title)
    body = html.escape(body)

    with connect_db() as db:
        sql = """
            INSERT INTO notes (title, body, pinned)
            VALUES (?, ?, ?)
        """
        params = (title, body, pinned)
        db.execute(sql, params)

        flash("Note added", "success")
        return redirect("/")

Updating Notes

# Show note edit form with existing data --------------------
@app.get("/note/<int:id>/edit")
def show_note_edit_form(id):
    with connect_db() as db:
        sql = """
            SELECT id, title, body, pinned, created
            FROM notes
            WHERE id=?
        """
        params = (id,)
        note = db.execute(sql, params).fetchone()

        return render_template("pages/note_form_edit.jinja", note=note)


# Process updated note --------------------------------------
@app.post("/note/<int:id>")
def update_a_note(id):
    title = request.form.get('title', '').strip()
    body = request.form.get('body', '').strip()

    pinned = bool(request.form.get('pin'))

    title = html.escape(title)
    body = html.escape(body)

    with connect_db() as db:
        sql = """
            UPDATE notes
            SET title=?, body=?, pinned=?
            WHERE id=?
        """
        params = (title, body, pinned, id)
        db.execute(sql, params)

        flash("Note updated", "success")
        return redirect("/")

Deleting Notes

# Delete a note ---------------------------------------------
@app.get("/note/<int:id>/delete")
def delete_a_note(id):
    with connect_db() as db:
        sql = """
            DELETE FROM notes
            WHERE id=?
        """
        params = (id,)
        db.execute(sql, params)

        flash("Note deleted", "success")
        return redirect("/")

Jinja Templates

notes_list.jinja

{% block content %}

    <section class="notes">
        {% for note in notes %}

            <article class="note {{ 'pinned' if note.pinned }}">

                <header>
                    <h3>{{ note.title }}</h3>
                    {{ "📌" if note.pinned }}
                </header>

                <p><a href="/note/{{ note.id }}">View note...</a></p>

            </article>

        {% else %}

            <p>No Notes!</p>

        {% endfor %}
    </section>

{% endblock %}  

note_single.jinja

{% block content %}

<article class="note {{ 'pinned' if note.pinned }}">

    <header>
        <h3>{{ note.title }}</h3>
        {{ "📌" if note.pinned }}
    </header>

    <p>{{ note.body | paragraphs }}</p>

    <footer>
        <span>Created on {{ note.created | local | format }}</span>

        <span><a href="/note/{{ note.id }}/edit">🖊️ Edit</a></span>
        <span><a href="/note/{{ note.id }}/delete"
                 onclick="return confirm('Really delete?')">🗑 Delete</a></span>
    </footer>

</article>

{% endblock %}  

note_form.jinja

{% block content %}

<article>

    <form method="post" action="/note">
        <h3>New Note</h3>

        <label>
            Title
            <input name="title" type="text" required>
        </label>

        <label>
            Body
            <textarea name="body" required></textarea>
        </label>

        <label>
            <input name="pin" type="checkbox">
            Pin note?
        </label>

        <button>Add Note</button>
    </form>

</article>

{% endblock %}  

note_form_edit.jinja

{% block content %}

<article>

    <form method="post" action="/note/{{ note.id }}">
        <h3>Edit Note</h3>

        <label>
            Title
            <input name="title" type="text" value="{{ note.title }}" required>
        </label>

        <label>
            Body
            <textarea name="body" required>{{ note.body }}</textarea>
        </label>

        <label>
            <input name="pin" type="checkbox" {{ 'checked' if note.pinned }}>
            Pin note?
        </label>

        <button>Update Note</button>
    </form>

</article>

{% endblock %}