You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Bokeh中TextInput的输入值传入MySQL查询语句?

问题描述

我正在用Bokeh搭配Flask,结合自建MySQL音乐数据库制作仪表盘,需求是根据用户输入的参数生成SQL查询语句,将结果存入DataFrame后在图表展示。现在卡在如何把TextInput组件的输入值传入以下SQL查询:

query = f'SELECT * FROM `tracks_per_year` WHERE tracktitle LIKE "{text_input_value}"'
track_search_df = dbconnect.run_query_to_df(query, connection=cnx)

我觉得Bokeh官方文档这部分说明不够,只展示了创建TextInput组件和在前端打印输入内容的示例,没讲怎么把值传到后端用于查询。官方示例代码如下:

from bokeh.io import show
from bokeh.models import CustomJS, TextInput

text_input = TextInput(value="default", title="Label:")
text_input.js_on_change("value", CustomJS(code="""
    console.log('text_input: value=' + this.value, this.toString())
"""))

show(text_input)

解决方案

根据Bokeh与Flask的集成方式,分两种场景实现输入值的获取与SQL查询对接:

方案1:Bokeh Server + Flask集成(后端Python回调)

适合需要复杂后端逻辑的场景,直接通过Python代码获取输入值并处理查询,无需前端JS操作。

实现步骤&代码示例

from bokeh.models import TextInput, Button, ColumnDataSource
from bokeh.plotting import figure
from bokeh.layouts import column
from bokeh.server.server import Server
from flask import Flask
import pandas as pd
import mysql.connector

# 初始化Flask应用
flask_app = Flask(__name__)

# MySQL连接配置(建议用环境变量存储敏感信息)
db_config = {
    'user': '你的用户名',
    'password': '你的密码',
    'host': 'localhost',
    'database': '你的音乐数据库名'
}

def bokeh_dashboard(doc):
    # 初始化图表数据源
    source = ColumnDataSource(data=dict(year=[], track_count=[]))
    
    # 创建输入组件和查询按钮
    track_input = TextInput(title="搜索曲目:", value="")
    search_btn = Button(label="查询", button_type="primary")
    
    # 创建示例图表(根据你的数据结构调整)
    p = figure(x_axis_label='年份', y_axis_label='曲目数量', title='每年曲目统计')
    p.line('year', 'track_count', source=source)
    
    # 查询回调函数:获取输入值、执行SQL、更新图表
    def update_chart():
        track_title = track_input.value.strip()
        if not track_title:
            return
        
        # 参数化查询:避免SQL注入风险,绝对不能直接用f-string拼接用户输入
        query = 'SELECT * FROM `tracks_per_year` WHERE tracktitle LIKE %s'
        # 模糊查询需添加通配符
        query_param = f'%{track_title}%'
        
        # 执行查询并更新数据源
        cnx = mysql.connector.connect(**db_config)
        try:
            df = pd.read_sql(query, cnx, params=(query_param,))
            source.data = df.to_dict('list')
        finally:
            cnx.close()
    
    # 绑定按钮点击事件到回调函数
    search_btn.on_click(update_chart)
    
    # 组装仪表盘布局
    doc.add_root(column(track_input, search_btn, p))

# 启动集成了Flask的Bokeh Server
server = Server({'/': bokeh_dashboard}, app=flask_app, port=5000)
server.start()

if __name__ == '__main__':
    server.io_loop.add_callback(server.show, "/")
    server.io_loop.start()

方案2:纯Flask路由+前端AJAX请求(无Bokeh Server)

适合轻量Flask应用,通过前端JS监听输入事件,发送请求到Flask接口获取数据后更新图表。

实现步骤&代码示例

Flask后端部分

from flask import Flask, request, jsonify
import pandas as pd
import mysql.connector

app = Flask(__name__)

db_config = {
    'user': '你的用户名',
    'password': '你的密码',
    'host': 'localhost',
    'database': '你的音乐数据库名'
}

# 查询API接口
@app.route('/search_tracks', methods=['POST'])
def search_tracks():
    data = request.get_json()
    track_title = data.get('track_title', '').strip()
    if not track_title:
        return jsonify([])
    
    # 参数化查询
    query = 'SELECT * FROM `tracks_per_year` WHERE tracktitle LIKE %s'
    query_param = f'%{track_title}%'
    
    cnx = mysql.connector.connect(**db_config)
    try:
        df = pd.read_sql(query, cnx, params=(query_param,))
        return jsonify(df.to_dict('records'))
    finally:
        cnx.close()

# 仪表盘页面路由
@app.route('/')
def index():
    from bokeh.models import TextInput, Button, ColumnDataSource, CustomJS
    from bokeh.plotting import figure
    from bokeh.layouts import column
    from bokeh.embed import components
    
    # 初始化数据源和图表
    source = ColumnDataSource(data=dict(year=[], track_count=[]))
    p = figure(x_axis_label='年份', y_axis_label='曲目数量', title='每年曲目统计')
    p.line('year', 'track_count', source=source)
    
    # 创建输入组件和按钮
    track_input = TextInput(title="搜索曲目:", value="")
    search_btn = Button(label="查询", button_type="primary")
    
    # 前端JS回调:发送AJAX请求并更新图表
    search_btn.js_on_click(CustomJS(args=dict(source=source, input=track_input), code="""
        const trackTitle = input.value.trim();
        if (!trackTitle) return;
        
        fetch('/search_tracks', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ track_title: trackTitle })
        })
        .then(response => response.json())
        .then(data => {
            // 转换数据格式适配Bokeh数据源
            const years = data.map(item => item.year);
            const counts = data.map(item => item.track_count);
            source.data = { year: years, track_count: counts };
        })
        .catch(error => console.error('查询出错:', error));
    """))
    
    # 生成Bokeh组件的嵌入代码
    script, div = components(column(track_input, search_btn, p))
    return f"""
    <html>
        <head>
            <link rel="stylesheet" href="https://cdn.bokeh.org/bokeh/release/bokeh-3.3.4.min.css" />
            <script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.3.4.min.js"></script>
        </head>
        <body>
            {div}
            {script}
        </body>
    </html>
    """

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

关键注意事项

  • 禁止直接用用户输入拼接SQL:必须使用参数化查询,否则会引发严重的SQL注入风险
  • 根据项目规模选择方案:复杂后端逻辑优先用Bokeh Server集成模式,轻量应用用AJAX模式更灵活
  • 建议使用数据库连接池,减少频繁创建/关闭连接的性能损耗

内容的提问来源于stack exchange,提问作者Joao Cerca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:08:17