miguelgrinberg / turbo-flask

Integration of Hotwire's Turbo library with Flask.
MIT License
301 stars 35 forks source link

Using Tubo-flask with Blueprints (app-context) #33

Open WillPowellUk opened 2 years ago

WillPowellUk commented 2 years ago

After following this tutorial on setting up turbo-flask, I have been unable to get it working with blueprints - I think because of app context.

To initialise turbo, I wanted to write turbo = Turbo(myBlueprint) however due to the error 'Blueprint' object has no attribute 'config' I imported app and initialised it like turbo = Turbo(app). Then for my update_load function I used with app.app_context().

The update_load function does iterate, however the current time on the page does not update without a manual page refresh. Here is the minimal code example, feel free to copy and run it. Any help much appreciated!

main.py

from website import create_app

app = create_app()

if __name__ == '__main__':
  app.run(debug=True)

init.py

from flask import Flask

app = Flask(__name__)

def create_app():
  from .myBlueprint import myBlueprint
  app.register_blueprint(myBlueprint, url_prefix='/')

  return app

myBlueprint.py

from flask import Blueprint, render_template, current_app
import time
from turbo_flask import Turbo
import threading

myBlueprint = Blueprint('myBlueprint', __name__)

from . import app
turbo = Turbo(app)

@myBlueprint.before_app_first_request
def before_first_request():
  threading.Thread(target=update_load).start()

@myBlueprint.route('/', methods=['GET', 'POST'])
def home():
  cTime = time.time()
  return render_template("home.html", currentTime=cTime)

def update_load():
  with app.app_context():
    while True:
      time.sleep(5)
      cTime = time.time()
      turbo.push(turbo.replace(render_template('home.html', currentTime=cTime), 'load'))

home.html

 <!DOCTYPE html>
    <html>
    <head>
      {{ turbo() }}
    </head> 
    <body>
      <h1> {{currentTime}} </h1>
    </body>
    </html>
miguelgrinberg commented 2 years ago

First of all, Turbo-Flask is a standard Flask extension. As all extensions, it is initialized with the app instance. You cannot initialize a Flask extension with a Blueprint instance, that just does not work.

My recommendation is that you initialize the turbo extension at the app level, not at the blueprint level, which forces you to import the app instance into the blueprint code. A good place to initialize the extension is in your app factory function.

Your push() call is indicating that you want to update an element with id="load". You don't have any elements in your page with this id. In fact, you aren't using any ids at all. You are also updating the full page, by pushing the same full-page template that renders the whole page. The idea is that a push updates a section of the page, not the whole page. Please review the load example in this repository to have a better idea of how this should work.

WillPowellUk commented 2 years ago

Thank you very much for your swift and helpful reply!

I was not aware that extensions cannot be initialised with a blueprint instance. After passing in the app instance to myBlueprint and adding the id, I have got this working.

Thank you for all your development with this extension.