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 methodlistto the class. After the__repr__method, start a new method by typingdef list(. Hitenterthen pressShift-Enterto start a new line.In the body of this method, type
return tand pressenterto accept the autocomplete oftodos.listhas a squiggly underline. Mouse-over it. PyCharm helpfully tells youMethod 'list' may be 'static'.Can PyCharm do this change for us? Click on
listand pressAlt-Enter. ChooseMake 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 propertydisplaythat can be overridden by subclasses.Click in the
.formatof__repr__(or anywhere on the return line) and chooseRefactor -> Extract -> Method.In the popup’s
Method name:box, enterdisplayand clickOK.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 indef displayand pressAlt-Enter. Sure enough, PyCharm can convert it to a property. SelectConvert 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 pressingCtrl-Wthree times. PyCharm’s selection should highlight the single quotes. (macOS:Alt-Up)Open the
Refactorpopup withCtrl-Alt-Shift-T. ChooseField. (macOS:Ctrl-T)In the inline popup, change
Initialize in:fromcurrent methodtoconstructor, then enterdisplay_fmtinto the red box and press enter.PyCharm has added
self.display_fmtto our constructor and changed thedisplayproperty method to use it.That’s a lot. Let’s re-run
models.pywithShift-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 insidelist_todos, typetodos = ToDoand pressAlt-Enterto import theToDoclass. Then continue typing.list(), resulting intodos = ToDo.list().Hmm, we have an unused import, as we don’t get
todosfrommodels.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 toTodo, and clickRefactor. Confirm that all cases were fixed in both files, then reload your browser to confirm the web app still works. Optionally, re-runmodels.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