Templates

Generating HTML from Python can be tiresome. Let’s put Jinja2 templates to work in Flask, while showing some PyCharm features such as Emmet for code completion. For now, though, we’ll just convert one view, the home page, to use a template.

Source for this step

Steps

  1. In app.py, let’s change the return value of home_page to return a template. Replace that line and instead, type return render_template and press Alt-Enter.

  2. Select import this name and select the first choice, from flask.

  3. Finish typing that line, resulting in:

    @app.route('/')
    def home_page():
        return render_template('index.html', title='Home Page')
    
  4. PyCharm warns us that Template file 'index.html' not found. Let’s create it.

  5. Right-click in the Project Tool window on the templates folder and select New -> File. Give it a name of index.html and click OK.

  6. Let’s use Emmet as a way to type fast. In the empty file, type html>head>title and press tab. PyCharm will generate much of the markup, leaving your cursor in the <title>.

  7. In the <title>, type Todo App: {{ title }} and press Shift-Enter to start a new line.

  8. Type body>h1 and press tab. Inside the generated <h1>, enter {{ title }} and press Shift-Enter.

  9. Type a and press tab. With the red box in the href, enter /todo/ and press enter. The cursor moves inside the <a>. Type Todos.

  10. Close the index.html tab.

  11. In app.py, note that the index.html is no longer a warning. Confirm that PyCharm can find the template by clicking in 'index.html' and pressing Ctrl-B. PyCharm opens index.html from the templates folder. (macOS: Cmd-B)

  12. Reload your browser on the root URL.

  13. Your app.py should match the following:

    app.py in Templates
    from flask import Flask
    from flask import render_template
    
    from models import populate_todos, Todo
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def home_page():
        return render_template('index.html', title='Home Page')
    
    
    @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/<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)
    
    
    if __name__ == '__main__':
        populate_todos()
        app.run(debug=True)
    
  14. Your templates/index.html should match the following:

    templates/index.html in Templates
    <html>
    <head><title>Todo App: {{ title }}</title></head>
    <body>
    <h1>{{ title }}</h1>
    <a href="/todo/">Todos</a>
    </body>
    </html>
    

Analysis

We added Flask template support, but saw a few last productivity features along the way:

  • Emmet support. PyCharm has a number of facilities for generating code. We previously saw Live Templates. Emmet is another. Postfix completion is a third way.
  • Templates directory. PyCharm warned us that a file didn’t exist in the Flask templates directory.

Extra Credit

  1. How did PyCharm know that index.html should be, then was, in a folder called templates?
  2. Besides HTML, where else can you use Emmet in PyCharm?