Flaxon 0.3.0 and Teloce-Py 0.3.0 are available on PyPI. Release instructions are maintained on this site; source links identify the framework snapshot. Check version and upgrade guidance.
06 | Task workflow
Goal
Add tasks, update status and due dates, filter tasks and delete them.
What you are building
Tasks belong to an owned project. list_tasks checks that project before querying its tasks. owned_task checks the parent project before permitting any mutation. A PATCH validates only supplied fields; allowed statuses are todo, doing and done. Date-only values use YYYY-MM-DD or null. The ORM handles values as parameters rather than SQL fragments. Deleting a project cascades through the database relationship.
Build this chapter
Stop the server. Work in project_manager. Follow each CREATE, whole-file replacement, or marked-block edit below in order.
Step 1: CREATE - make parent folders, then create this file - modules/tasks/init.py
This file owns the feature named by its module or component. Read the route/form flow before continuing.
Create the parent directories, then save this complete file.
"""Module-owned APIs and interface files."""
Say: "This file owns the feature named by its module or component. Read the route/form flow before continuing."
Type: Follow the action above in modules/tasks/__init__.py; explain each new function or binding as you add it.
Viewers should see: The saved file matches this step. After all steps, run the chapter checks below and show the expected result.
Step 2: CREATE - make parent folders, then create this file - modules/tasks/module.py
Create the tasks module. Require ownership of the parent project before listing, creating, editing or deleting tasks. Validate status and due dates before saving.
Create the parent directories, then save this complete file.
"""Tasks belong to projects; project ownership guards every task operation."""
from models import Task
from flaxon.modules import FlaxonModule
from flaxon.http import JSONResponse
from flaxon.exceptions import NotFound, BadRequest
from security import check_csrf
from validation import json_object, task_fields
from modules.projects.module import owned_project
tasks = FlaxonModule("tasks")
async def owned_task(request, task_id):
task = await Task.filter(id=task_id).first().values()
if task is None:
raise NotFound("Task not found.")
await owned_project(request, task["project_id"])
return task
@tasks.get("/project/<int:project_id>")
async def list_tasks(request, project_id):
await owned_project(request, project_id)
status = request.query.get("status")
if status and status not in ("todo", "doing", "done"):
raise BadRequest("Unknown task status.")
query = Task.filter(project_id=project_id)
if status:
query = query.filter(status=status)
return {"data": await query.order_by("-id").values()}
@tasks.post("/project/<int:project_id>")
async def create_task(request, project_id):
await check_csrf(request)
await owned_project(request, project_id)
data = task_fields(await json_object(request))
task = await Task.create(project_id=project_id, **data)
task_id = task.id
return JSONResponse({"data": await owned_task(request, task_id)}, status_code=201)
@tasks.patch("/<int:task_id>")
async def update_task(request, task_id):
await check_csrf(request)
current = await owned_task(request, task_id)
data = task_fields(await json_object(request), partial=True)
await Task.filter(id=task_id, project_id=current["project_id"]).update(**data)
return {"data": await owned_task(request, task_id)}
@tasks.delete("/<int:task_id>")
async def delete_task(request, task_id):
await check_csrf(request)
await owned_task(request, task_id)
await Task.filter(id=task_id).delete()
return {"data": {"deleted": True}}
Say: "Create the tasks module. Require ownership of the parent project before listing, creating, editing or deleting tasks. Validate status and due dates before saving."
Type: Follow the action above in modules/tasks/module.py; explain each new function or binding as you add it.
Viewers should see: The saved file matches this step. After all steps, run the chapter checks below and show the expected result.
Step 3: EDIT - replace the marked blocks - app.py
The application factory mounts only the features already introduced. Follow the action label: a marked-block edit keeps the surrounding factory intact.
Edit 1: find this exact block
Open app.py. Locate this block once. The unchanged surrounding lines identify its position; do not use line numbers from another revision.
from flaxon.http import JSONResponse
from modules.auth.module import auth
from modules.projects.module import projects
from modules.welcome.module import welcome
from settings import (
ROOT,
Replace that block with
from flaxon.http import JSONResponse
from modules.auth.module import auth
from modules.projects.module import projects
from modules.tasks.module import tasks
from modules.welcome.module import welcome
from settings import (
ROOT,
Leave the rest of the file unchanged.
Edit 2: find this exact block
Open app.py. Locate this block once. The unchanged surrounding lines identify its position; do not use line numbers from another revision.
for module, prefix in [
(auth, "/api/auth"),
(projects, "/api/projects"),
(welcome, "/api/welcome"),
]:
app.mount_module(module, prefix=prefix)
Replace that block with
for module, prefix in [
(auth, "/api/auth"),
(projects, "/api/projects"),
(tasks, "/api/tasks"),
(welcome, "/api/welcome"),
]:
app.mount_module(module, prefix=prefix)
Leave the rest of the file unchanged.
Say: "The application factory mounts only the features already introduced. Follow the action label: a marked-block edit keeps the surrounding factory intact."
Type: Follow the action above in app.py; explain each new function or binding as you add it.
Viewers should see: The saved file matches this step. After all steps, run the chapter checks below and show the expected result.
Run and check
python management.py runserver
# Exercise the task URLs shown in this chapter with your cookie and new CSRF token.
Exercise the task API now
Use the cookie and current CSRF token from chapter 4; replace PROJECT_ID with the ID returned by chapter 5. Then read the task list and change TASK_ID to the returned task ID:
curl -b cookies.txt -H 'Content-Type: application/json' -H 'X-CSRF-Token: TOKEN' -d '{"title":"Record the lesson","status":"todo","due_date":"2026-12-01"}' http://127.0.0.1:8000/api/tasks/project/PROJECT_ID
curl -b cookies.txt http://127.0.0.1:8000/api/tasks/project/PROJECT_ID
curl -X PATCH -b cookies.txt -H 'Content-Type: application/json' -H 'X-CSRF-Token: TOKEN' -d '{"status":"done"}' http://127.0.0.1:8000/api/tasks/TASK_ID
curl -b cookies.txt 'http://127.0.0.1:8000/api/tasks/project/PROJECT_ID?status=done'
Expected result
A signed-in customer can create a task, mark it done, filter by done and remove it. Invalid status and impossible date inputs return 400.
Common errors
Use /api/tasks/project/PROJECT_ID for task lists and creation. Use /api/tasks/TASK_ID for PATCH and DELETE. An empty PATCH and an unknown status are rejected.
Short exercise
Create two customers and prove one cannot update the other's task.
What to say and show
Say: "Add tasks, update status and due dates, filter tasks and delete them. The server remains responsible for persistence and authorization; the browser presents the result."
Show the expected result above, then point to the file responsible for it. Run the chapter check before the next lesson.
Save your checkpoint
git add .
git commit -m "Complete chapter 06: Task workflow"