Flask: unable to serve rendered pages from specific directory

Question:

I have a bunch of files in a directory that I wish to render and serve to the user, but have been unable to. Going to the path always returns just the 'page_template.htm' and not the rendered file. Here’s my code:

from flask import Flask, request, render_template, send_from_directory

# Instantiate the Flask app
app = Flask(__name__)
app.config.from_object(__name__)
pages = FlatPages(app)

@app.route("/")
@app.route("/index")
def index():
    return render_template('index.html')

@app.route("/Special_Data/<path:path>")
def page(path):
    page = send_from_directory('Special_Data', path)
    return render_template('page_template.htm', page=page)

What I wish to do is to grab raw text files from the 'Special_Data' directory and to render them into html files so they look nice, then send them to the user if they click on a link.

The files are in the directory 'Special_Data' and the 'page_template.htm' is in the 'templates' directory.

Where am I going wrong?

Asked By: Legion

||

Answers:

The problem is, that send_from_directory sends the file to the client, it does not load it into memory. So your page variable is actually of type Response instead of str or a fileobject. If you want to read the file and than display it use

import werkzeug

...

@app.route("/Special_Data/<path:path>")
def page(path):
    p = werkzeug.security.safe_join("Special_Data", path)
    with open(p, "r", encoding="utf-8") as f:
       return render_template('page_template.htm', page=f.read())

Otherwise I’m afraid I can’t help as I don’t know what you want to do.

Answered By: Bertik23

Using send_from_directory Return a file to client.
So, you need to change like this

@app.route("/Special_Data/<path:path>")
def page(path):
    return send_from_directory('Special_Data', path)

Or

   return send_file(filename_or_fp=file_path, as_attachment=True, add_etags=True, conditional=True)

render_template will render your html template, it may return a html string. Therefore you need return file instead HTML string

The following example shows you how you can use FlatPages to list, display and offer files for download. The markdown code is rendered beforehand and integrated into the specified or the default template.

from flask import Flask
from flask import render_template, send_file
from flask_flatpages import FlatPages
from io import BytesIO
import os

app = Flask(__name__)
# Optional configuration here!
pages = FlatPages(app)

# ...

# List all available pages.
@app.route('/contents')
def contents():
    return render_template('contents.html', pages=pages)

# Display the rendered result.
@app.route('/page/<path:path>')
def page(path):
    page = pages.get_or_404(path)
    template = page.meta.get('template', 'flatpage.html')
    return render_template(template, page=page)

# Download the rendered result.
@app.route('/download/<path:path>')
def download(path):
    page = pages.get_or_404(path)
    template = page.meta.get('template', 'flatpage.html')
    return send_file(
        BytesIO(str(render_template(template, page=page)).encode()),
        as_attachment=True,
        attachment_filename=f'{os.path.basename(path)}.html'
    )

templates/contents.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <ul>
      {% for page in pages %}
      <li>
        [<a href="{{ url_for('download', path=page.path) }}">Download</a>] - 
        <a href="{{ url_for('page', path=page.path) }}">{{ page.title }}</a>
      </li>
      {% endfor %}
    </ul>
  </body>
</html>

templates/flatpage.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{ page.title }}</title>
  </head>
  <body>
    {{ page }}
  </body>
</html>

pages/Special_Data/ncs1.html

title: Hello
published: 2010-12-22

Hello, *World*!

Lorem ipsum dolor sit amet

If you want to use a different template for rendering, you can define one within the metadata based on the file name. Add the following below published and a suitable template within the templates folder.

template: mytemplate.html
Answered By: Detlef
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.