Delete Todo¶
Deleting a todo is an important part of our application. Let’s add that feature while showing more about debugging:
- Run to Cursor
- Evaluate Expression
- Console
- Moving backwards in frames
Steps¶
This time we’re going to reverse our mode of implementation and start
instead with the UI side in app.py
.
In
app.py
, changelist_todos
to have a delete button:@app.route('/todo/') def list_todos(): todos = Todo.list() div = '''<div><a href="/todo/{id}/delete">{title}</a> <form method="POST" action="/todo/{id}/delete" style="display: inline"> <input type="submit" value="x"/> </form> </div>''' form = '''<form method="POST" action="add"> <input name="todo_id" placeholder="Add todo..."/> </form> ''' items = [div.format(id=t.id, title=t.title) for t in todos] items.append(form) return '\n'.join(items)
This form posts to a
/delete
route on a todo, so let’s add that route:@app.route('/todo/<int:todo_id>/delete', methods=['POST']) def delete_todo(todo_id): todo = Todo.get_id(todo_id) return redirect('/todo/')
Put a breakpoint on the first line inside this
delete_todo
route. Reload your browser (restarting your app in the debugger if necessary) and click on thex
for one of the todos.When execution stops in the route, confirm that
todo_id
is an integer.Click
Resume
to continue execution.In
models.py
, add a method for deletion:def delete(self): todos.remove(self)
Back in
app.py
, add a line todelete_todo
that calls this new method:@app.route('/todo/<int:todo_id>/delete', methods=['POST']) def delete_todo(todo_id): todo = Todo.get_id(todo_id) todo.delete() return redirect('/todo/')
Reload browser and click X to delete the second todo. Execution stops at your breakpoint.
Put your cursor in the
delete
method inmodels.py
.In the debugger, click
Run to Cursor
.Let’s make sure this is the correct todo. Click the
Evaluate Expression
button .In the popup window, enter
self.id == 2
to confirm that this is the correct todo. (Note: it might not be2
based on the sorting and random title generation.)Click the Debug Tool Window’s
Console
tab (beside theDebugger
tab), then click theShow Python Prompt
button .Let’s see what
todo_int
was defined as in the earlier stack frame by clickingdelete_todo
in theFrames
window.Variables
updates to show us the new scope.Click the
Resume
button .Your
models.py
should match the following:from random import choice todos = [] class Todo: def __init__(self, title): self.title = title self.display_fmt = 'Todo {todo_id}' self.id = max([todo.id for todo in todos], default=0) + 1 def __repr__(self): return self.display @property def display(self): return self.display_fmt.format(todo_id=self.id) @staticmethod def list(): return sorted(todos, key=lambda todo: todo.title) @staticmethod def add(title): todo = Todo(title) todos.append(todo) return todo def delete(self): todos.remove(self) @staticmethod def get_id(todo_id): return [todo for todo in todos if todo.id == todo_id][0] def populate_todos(): random_verbs = ['Make', 'Buy', 'Organize', 'Finish'] random_nouns = ['Red', 'Apple', 'Blue', 'Tomato', 'Green', 'Pear', 'Black', 'Bean', 'Yellow', 'Banana', 'Brown', 'Raisin'] for i in range(5): v = choice(random_verbs) w1 = choice(random_nouns) w2 = choice(random_nouns) title = '{v} {w1} {w2}'.format(v=v, w1=w1, w2=w2) Todo.add(title) if __name__ == '__main__': populate_todos() first_todo = Todo.list()[0] first_id = first_todo.id print(Todo.get_id(first_id))
Your
app.py
should match the following:from flask import Flask, request from flask import redirect from models import populate_todos, Todo app = Flask(__name__) @app.route('/') def home_page(): return 'Hello World! <a href="/todo/">Todos</a>' @app.route('/todo/') def list_todos(): todos = Todo.list() div = '''<div><a href="/todo/{id}/delete">{title}</a> <form method="POST" action="/todo/{id}/delete" style="display: inline"> <input type="submit" value="x"/> </form> </div>''' form = '''<form method="POST" action="add"> <input name="todo_id" placeholder="Add todo..."/> </form> ''' items = [div.format(id=t.id, title=t.title) for t in todos] items.append(form) return '\n'.join(items) @app.route('/todo/<int:todo_id>') def show_todo(todo_id): todo = Todo.get_id(todo_id) fmt = '<h1>Todo {todo_id}</h1><p>{title}</p>' return fmt.format(todo_id=todo.id, title=todo.title) @app.route('/todo/add', methods=['POST']) def add_todo(): todo_id = request.form['todo_id'] if todo_id: Todo.add(todo_id) return redirect('/todo/') @app.route('/todo/<int:todo_id>/delete', methods=['POST']) def delete_todo(todo_id): todo = Todo.get_id(todo_id) todo.delete() return redirect('/todo/') if __name__ == '__main__': populate_todos() app.run(debug=True)
- Previous topic: Sorted Listings
- Next topic: Change Display