miguelgrinberg / turbo-flask

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

Use Turbo with Blueprint? #7

Closed yoyoliyang closed 3 years ago

yoyoliyang commented 3 years ago

As the title. When I use Turbo with Blueprint like this: ` bp = Blueprint(......)

bp = Turbo(bp) ` It can't work. so, what should I do?

miguelgrinberg commented 3 years ago

Turbo is a Flask extension, it should be used on the application instance.

yoyoliyang commented 3 years ago

Thank you!(I bought your book in my contry, I'm from CHINA. :-), and your book is very useful!

Well, I want use it in blueprint, my code was like this:

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

bp = Blueprint('cpu_load_avages', __name__, static_folder="../templates", url_prefix='/cpu')

@bp.route('/')
async def index():
    return render_template('cpu.html')

@bp.context_processor
def inject_load():
    load = [0,0,0]
    if sys.platform.startswith('linux'):
        with open('/proc/loadavg', 'rt') as f:
            load = f.read().split()[0:3]

    return {'load1': load[0], 'load5': load[1], 'load15': load[2]}

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

def update_load():
    with current_app.app_context():
        turbo = Turbo(current_app)
        while True:
            time.sleep(5)
            turbo.push(turbo.replace(render_template('cpu.html'), 'load'))

but there was wrong with this:

File "/home/yang/.local/share/virtualenvs/flask_tutorial-fiBXDnIR/lib/python3.7/site-packages/flask/globals.py", line 47, in _find_app
    raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.

So what should I do? I seem to use Blueprint to keep the architecture clear. Thank you very much!

miguelgrinberg commented 3 years ago

You can use it anywhere in your app, including in a blueprint, but as a Flask extension it should be initialized with the Flask application instance, not with a particular blueprint.

yoyoliyang commented 3 years ago

Thank you for replay. Sorry my bad english, I think I should use app_context function to create a context environment in blueprint file, my code like this, can you help me?

app extensions.py

from turbo-flask import Turbo
turbo = Turbo

app __init__.py

from flask import Flask
from server_monitor.extensions import turbo
from server_monitor.utils import smart, ups, cpu

def create_app():

    app = Flask(__name__)
    app.register_blueprint(cpu.bp) # the blueprint who use turbo

    # extensions
    turbo.init_app(app)

    return app

and cpu.py

from flask import Blueprint, render_template
import sys
import threading
import time
from server_monitor.extensions import turbo

bp = Blueprint('cpu_load_avages', __name__, static_folder="../templates", url_prefix='/cpu')

@bp.route('/')
def index():
    return render_template('cpu.html')

@bp.context_processor
def inject_load():
    load = [0,0,0]
    if sys.platform.startswith('linux'):
        with open('/proc/loadavg', 'rt') as f:
            load = f.read().split()[0:3]

    return {'load1': load[0], 'load5': load[1], 'load15': load[2]}

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

def update_load():
    # how to use app_context() in there but with blueprint not Flask app
    while True:
        time.sleep(5)
        turbo.push(turbo.replace(render_template('cpu.html'), 'load'))
miguelgrinberg commented 3 years ago
from flask import current_app

# ...

def update_load():
    with current_app.app_context():
        while True:
            time.sleep(5)
            turbo.push(turbo.replace(render_template('cpu.html'), 'load'))
yoyoliyang commented 3 years ago

I got some error. My code:

from flask import Blueprint, render_template, current_app
import sys
import threading
import time
from server_monitor.extensions import turbo

bp = Blueprint('cpu_load_avages', __name__, static_folder="../templates", url_prefix='/cpu')

@bp.route('/')
def index():
    return render_template('cpu.html')

@bp.context_processor
def inject_load():
    load = [0,0,0]
    if sys.platform.startswith('linux'):
        with open('/proc/loadavg', 'rt') as f:
            load = f.read().split()[0:3]

    return {'load1': load[0], 'load5': load[1], 'load15': load[2]}

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

def update_load():
    with current_app.app_context():
        while True:
            time.sleep(5)
            turbo.push(turbo.replace(render_template('cpu.html'), 'load'))
Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "/home/yang/flask_tutorial/server_monitor/utils/cpu.py", line 28, in update_load
    with current_app.app_context():
  File "/home/yang/.local/share/virtualenvs/flask_tutorial-fiBXDnIR/lib/python3.7/site-packages/werkzeug/local.py", line 422, in __get__
    obj = instance._get_current_object()
  File "/home/yang/.local/share/virtualenvs/flask_tutorial-fiBXDnIR/lib/python3.7/site-packages/werkzeug/local.py", line 544, in _get_current_object
    return self.__local()  # type: ignore
  File "/home/yang/.local/share/virtualenvs/flask_tutorial-fiBXDnIR/lib/python3.7/site-packages/flask/globals.py", line 47, in _find_app
    raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.
miguelgrinberg commented 3 years ago

Oh yeah, sorry, I did not realize this was running in a thread. Do it like this:

@bp.before_app_first_request
def before_first_request():
    threading.Thread(target=update_load, args=(current_app._get_current_object(),)).start()

def update_load(app):
    with app.app_context():
        while True:
            time.sleep(5)
            turbo.push(turbo.replace(render_template('cpu.html'), 'load'))
yoyoliyang commented 3 years ago

Thank you, The problem is solved. Thank you very much.

EmmanuelBrache commented 1 year ago

Hello, I try to relaod this topic. I know some mistakes with turbo_flask. When i use it with a simple app with no factory (create_app) in a single file , it's ok.

But with blueprints and a wsgi mode (apache) it doesn't work. So can you help me.

skeleton of my app

image

I follow the api's doc. I use threading with blueprint.

app.wsgi (classic not amazing)


import sys
sys.path.insert(0, '/appli/torbu')
from api import create_app
application = create_app()

init.py factory


def create_app():
    app = Flask(__name__)
    app.register_blueprint(bp_order) # the blueprint who use turbo
    # extensions
    turbo.init_app(app)
    return app

order/routes.py

from flask import Blueprint, render_template, current_app
import sys
import threading
import time
from api.extensions import turbo

bp_order = Blueprint('bp_order', __name__, static_folder="../templates", url_prefix='/order')

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

@bp_order.context_processor
def inject_time():
    from datetime import datetime
    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")
    return {'timenow': current_time}

@bp_order.before_app_first_request
def before_first_request():
    threading.Thread(target=updatetime, args=(current_app._get_current_object(),)).start()

def updatetime(app):
    with app.app_context():
        while True:
            time.sleep(1)
            turbo.push(turbo.replace(render_template('loadtime.html'), 'loadtime'))

In my log apache

 ERROR in app: Exception on /turbo-stream [GET]
Traceback (most recent call last):
  File "/home/emmanuel/.local/lib/python3.10/site-packages/flask/app.py", line 2528, in wsgi_app
   response = self.full_dispatch_request()
  File "/home/emmanuel/.local/lib/python3.10/site-packages/flask/app.py", line 1825, in full_dispatch_request
   rv = self.handle_user_exception(e)
  File "/home/emmanuel/.local/lib/python3.10/site-packages/flask/app.py", line 1823, in full_dispatch_request
     rv = self.dispatch_request()
  File "/home/emmanuel/.local/lib/python3.10/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "/home/emmanuel/.local/lib/python3.10/site-packages/flask_sock/__init__.py", line 59, in websocket_route
    ws = Server(request.environ, **current_app.config.get(
  File "/home/emmanuel/.local/lib/python3.10/site-packages/simple_websocket/ws.py", line 319, in __init__
    raise RuntimeError('Cannot obtain socket from WSGI environment.')
RuntimeError: Cannot obtain socket from WSGI environment.

Can you help me please? Someone?

Thanks.

miguelgrinberg commented 1 year ago

Apache is not a supported web server for the Flask-Sock extension, which Turbo-Flask depends on.

EmmanuelBrache commented 1 year ago

Thanks. Do you known solution (extension) like turbo_f ? On a flask webservice, i want update different part of page.

EmmanuelBrache commented 1 year ago

Ok, i try with the embedded flask http (flask ... run). My page is correctly print at first load. image

but my element html is not update. I insert a print(ok) during the update and it's printing.

the turbo.push line don't work (no error, nothing in page :( )

My code :

from flask import Blueprint, render_template, current_app
import sys
import threading
import time

sys.path.insert(0, '/appli/torbu')
from api.extensions import turbo

bp_order = Blueprint('bp_order', __name__, static_folder="../templates", url_prefix='/order')

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

@bp_order.context_processor
def inject_time():
    from datetime import datetime
    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")
    return {'loadtime': current_time}

@bp_order.before_app_first_request
def before_first_request():
    threading.Thread(target=updatetime, args=(current_app._get_current_object(),)).start()

def updatetime(app):
    with app.app_context():
        # how to use app_context() in there but with blueprint not Flask app
        while True:
            time.sleep(1)
            print("ok")
            turbo.push(turbo.replace(render_template('loadtime.html'), 'loadtime'))
            print("pass")

image

miguelgrinberg commented 1 year ago

@EmmanuelBrache your code may or may not have problems, but as I said above, you can't use Apache with Turbo-Flask. See the docs: https://turbo-flask.readthedocs.io/en/latest/quickstart.html#deployment.

EmmanuelBrache commented 1 year ago

Ok but my last post is used with flask:run (localhost:5000) :) sorry

miguelgrinberg commented 1 year ago

I can't really visually debug your application, sorry. Do the examples in this repository work for you? If yes, start from one of them and modify it to suit your needs.