Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Turbo-Flask to dynamically update <select> options from a database? #22

Closed
ghost opened this issue Jan 15, 2022 · 6 comments
Closed
Labels
question Further information is requested

Comments

@ghost
Copy link

ghost commented Jan 15, 2022

Hi, thanks for this library, it's looking like it will solve an issue that I have, I just can't get my head around how right now!

I'm working on a platform where devices can register themselves with a central database using MQTT. Users then select a device for a time-limited session, and assign it to a location via a web interface.

I've got the devices registering perfectly fine with the backend database, and my plan was to use Turbo-Flask to either:

  1. Poll the database every second looking for new devices
  2. Listen to the /register MQTT channel wait for new devices

Turbo-flask would then update a form select dropdown in the UI so that the user can select that device.

I've got the LoadAVG code working fine, I'm just struggling slightly to understand how I update a single form element rather than reloading an entire template!

@miguelgrinberg
Copy link
Owner

Give your select field an id attribute, then you can send a new version of it with a different list of options as an update.

@miguelgrinberg miguelgrinberg added the question Further information is requested label Jan 15, 2022
@ghost
Copy link
Author

ghost commented Jan 16, 2022

OK, thanks, so the template would look like

<!-- template/options.html -->
<select id="mySelect">
{% for option in data %}
<option value="{{ option.value }}">{{option.name}}</option>
{% endfor %}

and the call to Turbo-Flask

turbo.push(turbo.replace(render_template('options.html'), 'mySelect'))

?

@miguelgrinberg
Copy link
Owner

Yeah, I think that should do it. But you need to close your </select>.

@ghost
Copy link
Author

ghost commented Jan 19, 2022

OK, I'm getting a lot closer, however I'm now seeing a context error (TypeError: cannot convert dictionary update sequence element #0 to a sequence) when I try and run the code.

I think this is because my turbo-flask call is in a blueprint:

# extensions.py
# Initialise Turbo for websocket connectivity
from turbo_flask import Turbo
turbo = Turbo()
# devices.py
import logging
import sys
import threading
import time
import uuid

from flask import Blueprint, render_template, current_app
from flask_login import login_required, current_user

from . import db
from .models import *
from .extensions import turbo

devices = Blueprint('devices', __name__)

@devices.app_context_processor
def get_devices():
    urd = []
    for d in RegisteredDevices.query.filter(RegisteredDevices.device_is_registered == False).all():
        urd.append(d)
    return urd


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

def device_updates(app):
    with app.app_context():
        while True:
            print("Publishing...")
            time.sleep(5)
            turbo.push(turbo.replace(render_template('session_devices.html'), 'unregistered_devices'))
# __init__.py
import os 
import socket

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from .extensions import turbo


# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()

def create_app():
    app = Flask(__name__)

    app.config['SERVER_NAME'] = os.getenv("APP_SERVER_NAME")
    app.config['SECRET_KEY'] = os.getenv("APP_SECRET_KEY")
    app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("APP_DB_URI")

    db.init_app(app)
    turbo.init_app(app)

    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)

    from .models import User

    @login_manager.user_loader
    def load_user(user_id):
        # since the user_id is just the primary key of our user table, use it in the query for the user
        return User.query.get(int(user_id))

    # blueprint for auth routes in our app
    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint)

    # blueprint for non-auth parts of app
    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    # blueprint for session parts of app
    from .devices import devices as devices_blueprint
    app.register_blueprint(devices_blueprint)

    return app

When I visit any URI, I get the following error on the screen and in the logs:

127.0.0.1 - - [19/Jan/2022 07:20:54] "GET /devices HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 2091, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 2076, in wsgi_app
    response = self.handle_exception(e)
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 1518, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 1516, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 1502, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask_login/utils.py", line 272, in decorated_view
    return func(*args, **kwargs)
  File "/home/ff/device_test/devices.py", line 32, in index
    return render_template(
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/templating.py", line 146, in render_template
    ctx.app.update_template_context(context)
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 756, in update_template_context
    context.update(func())
TypeError: cannot convert dictionary update sequence element #0 to a sequence
127.0.0.1 - - [19/Jan/2022 07:20:54] "GET /devices?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [19/Jan/2022 07:20:54] "GET /devices?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [19/Jan/2022 07:20:54] "GET /devices?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [19/Jan/2022 07:20:54] "GET /devices?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/lib/python3.9/threading.py", line 973, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.9/threading.py", line 910, in run
    self._target(*self._args, **self._kwargs)
  File "/home/ff/device_test/devices.py", line 70, in device_updates
    turbo.push(turbo.replace(render_template('session_devices.html'), 'unregistered_devices'))
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/templating.py", line 146, in render_template
    ctx.app.update_template_context(context)
  File "/home/ff/.local/share/virtualenvs/device_test/lib/python3.9/site-packages/flask/app.py", line 756, in update_template_context
    context.update(func())
TypeError: cannot convert dictionary update sequence element #0 to a sequence

It works fine until I try to push the DB query results to the turbo front-end, do I need to re-marshall the device data into an different dict?

@miguelgrinberg
Copy link
Owner

Your template context function appears to be returning something that is not a dict.

@ghost
Copy link
Author

ghost commented Jan 19, 2022

OK, thanks, I'll close this off for now and debug the template issue! :)

@ghost ghost closed this as completed Jan 19, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant