Todo Class

We’re going to start a Todo class, to replace each dictionary in our todos` list. We’ll let PyCharm help us with this code refactoring, including changing our app.py web app to use model.todos.

Source for this step

Steps

  1. Near the top, under todos = [], let’s define a class. Type the following and stop at that point:

    class ToDo:
        def in
    

    ...and hit tab. PyCharm will complete the __init__(self):.

    Note

    Yes, we said ToDo instead of Todo. We will come back to clean that up later.

  2. Left-arrow back into the constructor, after self, and change it to (self, title):.

  3. With the cursor still on the e in title, press Alt-Enter.

  4. Choose Add field 'title' to class ToDo from the Quick Fix list.

  5. With the red box outlining .title, press enter to accept the suggested field name and jump to the end of the line.

  6. On the next line in the constructor, add self.id = randint(1000, 9999) and, when your cursor is after the last 9, press Shift-Enter to Start New Line after the end of the current line.

    Note: By generating random ids, every restart will provide new URLs when going to the show_todo URL. Keep this in mind. You will have to go back to the ``list_todos`` URL, reload, and click a link.

  7. Click in randint, press Alt-Enter, and choose Import 'random.randint'. To move there from the end of the line, you can use Ctrl-Left to jump by word. (macOS: Alt-Left)

  8. As always, Ctrl-Alt-L to clean up formatting. (macOS: Cmd-Alt-L)

  9. Make a Python representation by starting a new method:

    def rep
    

    ...and hit tab. PyCharm will complete the __repr__(self):.

  10. Finish the method with return 'Todo {todo_id}'.format(todo_id=self.id).

  11. Change populate_todos to append instances: todos.append(ToDo('First')).

  12. Change the print statement at the bottom to print(todos).

  13. Re-run to confirm, using Shift-F10. (macOS: Ctrl-R)

  14. Our app.py needs to get its todos from models.py instead of keeping its own list.

  15. Remove the todos assignment line from app.py.

  16. Add an import, after the first import:

    from models import todos
    

    Remember to use autocomplete on models and todos

  17. list_todos is using dictionary access. Let’s convert it to attribute access:

    items = [div.format(id=t.id, title=t.title) for t in todos]
    

    Remember to use autocomplete on ``.format``!

  18. Finally, we need to populate our todos. In the main block at the bottom for app.py, start a new line and type populate_todos.

  19. Before adding parentheses, generate the import by pressing Alt-Enter and choosing the first choice. Then add ().

  20. Now reload your browser and you should see your list of one todo.

  21. Your app.py should match the following:

    app.py in Todo Class
    from flask import Flask
    from models import todos, populate_todos
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def home_page():
        return 'Hello World! <a href="/todo/">Todos</a>'
    
    
    @app.route('/todo/')
    def list_todos():
        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)
    
  22. Your models.py should match the following:

    models.py in Todo Class
    from random import randint
    
    todos = []
    
    
    class ToDo:
        def __init__(self, title):
            self.title = title
            self.id = randint(1000, 9999)
    
        def __repr__(self):
            return 'Todo {todo_id}'.format(todo_id=self.id)
    
    
    def populate_todos():
        todos.append(ToDo('First'))
    
    
    if __name__ == '__main__':
        populate_todos()
        print(todos)
    

Analysis

We did quite a lot in this step, letting PyCharm help us on productivity.

  • Autocomplete. PyCharm handled a lot of typing for us on __init__ and __repr__, as well as .format. Even if it isn’t a lot of characters, it’s better to let PyCharm do the completion, to avoid typos and add in the parens, self, etc.
  • Refactoring. The “Add field ‘title’ to class ToDo” refactoring was quite helpful. This happens in constructors and methods often.
  • Generate imports. It’s nice to let PyCharm generate the import just by using a symbol. Not only does it generate the import, but it leaves your cursor exactly where you left it.

Extra Credit

  1. If you have to stop your editing and go somewhere to add/fix a line, what is a quick way to jump back to where you were at?
  2. Does PyCharm have a Code Intention to convert dictionary access (todo['id']) to attribute access (todo.id)?