It's not perfect, but I am trying out the following. I have a decorator:
def early_flush_of_head(template_name):
def wrapper(original_view_function):
@wraps(original_view_function)
def new_view_function(*args, **kwargs):
def streamer():
yield render_template(template_name, head_only=True)
yield original_view_function(*args, **kwargs)
return Response(stream_with_context(streamer()))
return new_view_function
return wrapper
You can use it to decorate a given view like this:
@app.route('/')
@early_flush_of_head('template.html')
def index():
data = something_time_consuming() # DB traffic or maybe some crazy calculations
return render_template('template.html', data=data)
Each template.html looks like this:
{% extends "base.html" %}
{% block pre_flush_head %}
<link href="static/css/page.min.css" rel="stylesheet">
<script src="static/js/page.min.js"></script>
{% endblock %}
{% block post_flush_head %}
<title>{{ page_name }}</title>
{% endblock %}
{% block content %}
The actual page.
{% endblock %}
base.html looks like this:
{% if head_only %}
<!DOCTYPE html>
<html lang="en">
<head>
<link href="static/css/common.min.css" rel="stylesheet">
<script src="static/js/common.min.js"></script>
{% else %}
{% block post_flush_head %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
{% endif %}
Each template is actually rendered twice. Once in the decorator with head_only=True, then again without this defined in the real view function. Note also that I don't flush the whole head, there is some stuff that goes in the head but still needs the data from the real view function. Title is a good example if you are naming the page based on data.
There is some room for improvement here (I don't like that you have to put the name of the template twice, and it is overall a bit intrusive), but it does get you the benefit of an early flush (make sure your deployment environment doesn't buffer without your knowledge).