List Todos Method¶
Having our web app import the todos list directly isn’t that smart. What if there’s some work involved, such as a database query? Let’s change to use a class method. We can get PyCharm to help us with the refactoring.
Steps¶
- In - models.py, let’s add a method- listto the class. After the- __repr__method, start a new method by typing- def list(. Hit- enterthen press- Shift-Enterto start a new line.
- In the body of this method, type - return tand press- enterto accept the autocomplete of- todos.
- listhas a squiggly underline. Mouse-over it. PyCharm helpfully tells you- Method 'list' may be 'static'.
- Can PyCharm do this change for us? Click on - listand press- Alt-Enter. Choose- Make method static.
- Press - Ctrl-Alt-Lto clean up any line formatting complaints. (macOS:- Cmd-Alt-L)
- Our - __repr__is doing a lot. Perhaps we can refactor it. First, let’s extract the formatting into a property- displaythat can be overridden by subclasses.
- Click in the - .formatof- __repr__(or anywhere on the return line) and choose- Refactor -> Extract -> Method.
- In the popup’s - Method name:box, enter- displayand click- OK.
- PyCharm makes a new - displaymethod with the logic, and changes- __repr__to call it.
- Let’s say you changed your mind. Press - Ctrl-Zto undo and all of that work is undone in one unit. (macOS:- Cmd-Z)
- Or you decided you wanted it. Press - Shift-Cmd-Zto redo. (macOS:- Shift-Cmd-Z)
- Something looks fishy about - display. Click somewhere in- def displayand press- Alt-Enter. Sure enough, PyCharm can convert it to a property. Select- Convert method to property. PyCharm adds the decorator and changes all usages, such as- __repr__.
- Next, we’d like the format string to be parameterizable. Click once inside - 'Todo {todo_id}'then expand the selection by pressing- Ctrl-Wthree times. PyCharm’s selection should highlight the single quotes. (macOS:- Alt-Up)
- Open the - Refactorpopup with- Ctrl-Alt-Shift-T. Choose- Field. (macOS:- Ctrl-T)
- In the inline popup, change - Initialize in:from- current methodto- constructor, then enter- display_fmtinto the red box and press enter.
- PyCharm has added - self.display_fmtto our constructor and changed the- displayproperty method to use it.
- That’s a lot. Let’s re-run - models.pywith- Shift-F10to make sure it runs ok. Make sure the models.py run configuration is selected. (macOS:- Ctrl-R)
- We can now refactor - app.pyto use this. On the first line inside- list_todos, type- todos = ToDoand press- Alt-Enterto import the- ToDoclass. Then continue typing- .list(), resulting in- todos = ToDo.list().
- Hmm, we have an unused import, as we don’t get - todosfrom- models.pyany longer. PyCharm can help.- Code -> Optimize Importsnot only removes unused imports, but re-organizes your import listing based on a configurable policy.
- Does the web app still work? Reload the browser and visit the todo listing to confirm. 
- At long last, we realize our - ToDotypo, and by now, it is used in multiple places in multiple files. PyCharm’s Rename Refactoring to the rescue!
- Click in an occurrence of - ToDo.
- Click on - Refactor -> Rename, change to- Todo, and click- Refactor. Confirm that all cases were fixed in both files, then reload your browser to confirm the web app still works. Optionally, re-run- models.py.
- Your - app.pyshould match the following:app.py in List Todos Method¶- from flask import Flask 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>' items = [div.format(id=t.id, title=t.title) for t in todos] return '\n'.join(items) @app.route('/todo/<todo_id>') def show_todo(todo_id): return 'Todo {todo_id}'.format(todo_id=todo_id) if __name__ == '__main__': populate_todos() app.run(debug=True) 
- Your - models.pyshould match the following:models.py in List Todos Method¶- from random import randint todos = [] class Todo: def __init__(self, title): self.display_fmt = 'Todo {todo_id}' self.title = title self.id = randint(1000, 9999) def __repr__(self): return self.display @property def display(self): return self.display_fmt.format(todo_id=self.id) @staticmethod def list(): return todos def populate_todos(): todos.append(Todo('First')) if __name__ == '__main__': populate_todos() print(todos) 
Analysis¶
Another step with quite a number of small changes:
- Refactoring static method. The conversion to a static method is not only a useful warning, but doing the work for us lets us complete the task without much interruption.
- Logic refactoring. The sequence of extracting the logic to a method, converting it for us into a property, then extracting the format string to a constructor field, really showed how PyCharm refactoring can keep us in the development flow.
- Use, then auto-import. We again see the pattern of typing in a symbol, then letting PyCharm generate the import.
- Optimize Imports. Cleaning up your imports is tedious, janitorial work. Let PyCharm do it for you. New in PyCharm 2016.2: configure how the imports are laid out, to match your preferences.
- Project-wide Rename. Sometimes you avoid a rename, just to avoid all the work to find the symbol and fix it. PyCharm makes this refactoring a trivial task.
Extra Credit¶
- We use Alt-Shift-Upto extend the selection. Can this extend beyond the current line, to an entire block?
- In Python, what’s the difference between @classmethodand@staticmethod?
- Previous topic: Todo Class
- Next topic: Single Todo