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_todosto 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
/deleteroute 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_todoroute. Reload your browser (restarting your app in the debugger if necessary) and click on thexfor one of the todos.When execution stops in the route, confirm that
todo_idis 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_todothat 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
deletemethod inmodels.py.In the debugger, click
Run to Cursor
.Let’s make sure this is the correct todo. Click the
Evaluate Expressionbutton
.In the popup window, enter
self.id == 2to confirm that this is the correct todo. (Note: it might not be2based on the sorting and random title generation.)Click the Debug Tool Window’s
Consoletab (beside theDebuggertab), then click theShow Python Promptbutton
.Let’s see what
todo_intwas defined as in the earlier stack frame by clickingdelete_todoin theFrameswindow.Variablesupdates to show us the new scope.Click the
Resumebutton
.Your
models.pyshould match the following:models.py in Delete Todo¶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.pyshould match the following:app.py in Delete Todo¶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