Sorted Listings¶
Our listings appear in the order they were added, not in alphabetical order. Let’s fix that, while making a bigger database of random todos. We’ll use this to show more debugging features:
- Break on exceptions
- Step Over
- Step Into
- Step Into My Code
Steps¶
First, make sure you have no breakpoints open and your application isn’t currently stopped at a breakpoint.
Let’s change
populate_todos
to dynamically make some synthetic data, at first with a typo that we will later correct: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_nounsxxx) title = '{v} {w1} {w2}'.format(v=v, w1=w1, w2=w2) Todo.add(title)
When you save, Flask restarts, runs
populate_todos
, and has aNameError
on startup. You can see the traceback in theDebugger
console tab.Let’s change the debugger to stop on exceptions by clicking
Run -> View Breakpoints
.In the popup, select
Python Exception Breakpoint -> Any Breakpoint
and click the checkbox forOn raise
.Re-run debugger by clicking the green
Re-run
button in the5. Debug
tool window’s toolbar.This time the debugger stops, but in library code. Let’s fix that.
Go back to
Run -> View Breakpoints
, thenPython Exception Breakpoint -> Any Breakpoint
and click the checkbox forIgnore Library Files
.Re-run debugger by clicking and execution stops on the exception but in our code.
Fix the typo by removing
xxx
and click the resume button .Let’s step through execution. Set a breakpoint on
v = choice(random_verbs)
by clicking in the left margin beside that line to create a red circle.Click the
Re-run
button to restart. Execution will stop on that line, the first time through thefor
loop.Click the
Step Over
button then click it again.Clear your breakpoint by clicking on the red circle, then click
Resume
.Let’s use Step Into to go
populate_todos
. Set a breakpoint on the line for near the end forpopulate_todos()
.Re-run our debug session by clicking
Re-run
. Execution stops on that line.Click
Step Into
to go intopopulate_todos
. PyCharm moves the highlighted line to the first line inside that function.Click
Step Into
six more times. When you reachv = choice(random_verbs)
, the debugger “steps into” thechoice
function of Python’srandom
modules. Sometimes that sucks, so let’s do something different.Click
Step Out
to get out of descending intochoice
. We’re back to the surface, inv = choice(random_verbs)
.This time, click
Step Into My Code
and click a couple more times. Note that we jump overchoice
, because it isnt part of this project’s code.Remove the breakpoint and click
Resume
.Now that we have a synthetic list, let’s sort it. Change our
Todo
class’s methoddef list
toreturn sorted(todos, key=lambda todo: todo.title)
.Make sure our debug session is still running, then reload your browser on the todo listing page. Note that the todos are sorted.
Re-run your debug session by clicking and reload your browser. Different todo titles, but sorted.
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 @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}">{title}</a></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/') if __name__ == '__main__': populate_todos() app.run(debug=True)
- Previous topic: Add Todo
- Next topic: Delete Todo