update the dropdown without using submit button

Question:

Here is a flask application

Use this link to run the application : (Replace XXX with your machine url) http://XXX.0.0.1:5050/?tickers_get=Company2&open_price_get=23

What i am trying is, when the user selects Company2 from the dropdown, the other dropdown should get updated to only 2 prices (22 and 40) without hitting submit button. Then when the user clicks on Submit button, the table should be displayed according

app.py

from flask import Flask, render_template, request
import pandas as pd
import sqlalchemy as sal
from sqlalchemy import create_engine
import pyodbc 
import urllib
import numpy as np

app = Flask(__name__)

get_data_through = "manual_entry"

@app.route('/')
def index():
    
        read_df = pd.DataFrame(
            [
                ['Company1', 23, 10000],
                ['Company2', 22, 40000],
                ['Company2', 40, 40000]
            ],
            columns=['new_sql_table','Open_price', 'volume']
        )
        names = set(read_df['new_sql_table'].tolist())    
        tickers = request.args.getlist('tickers_get')
        #tickers_get_to_string = ''.join(tickers)  # to convert to string
        
        #open_price_to_filter = np.arange(1000).tolist()
        open_price_to_filter = read_df['Open_price'].tolist()  
        open_price = request.args.getlist('open_price_get')        
        print(open_price)
        open_price_get_to_number = ''.join(open_price)
        data = read_df[read_df['new_sql_table'].isin(tickers)]
        sum_of_volumns = format(sum(data['volume']), ',')
        return render_template('template.html', **locals())   


if __name__ == "__main__":
    app.run(port = 5050)

template.html


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles/mainpage.css') }}">
    <title>Stocks data</title>
</head>
<body>
<h2>Stocks data</h2><h4>Total Volume is : {{sum_of_volumns}} and selected open price is {{open_price_get_to_number}}</h4> 
    <form>
        <label>Company:</label>
        <select name="tickers_get">
            {% for name in names %}
            <option value="{{ name }}" {% if name in tickers %}selected{% endif %}>{{name}}</option>
            {% endfor %}
        </select>
        
        
        <label>Open Price:</label>
        <select name="open_price_get" style="width:75px">         
            {% for op in open_price_to_filter %}
            <option value="{{ op }}" {% if op in open_price %}selected{% endif %}>{{op}}</option>
            {% endfor %}
        </select>
        
        <button type="submit">Submit</button>
    </form>
    <hr/>

    <table border="2" width="100%">
        <thead>
            <tr>
                {% for column in data.columns %}
                <th>{{ column }}</th>
                {% endfor %}
            </tr>
        </thead>
        <tbody>
            {% for row in data.values %}
            <tr style="text-align:center">
                {% for cell in row %}
                <td>{{ cell }}</td>
                {% endfor %}
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>

I am not able to get the dynamic dropdown in the second filter

Asked By: navee pp

||

Answers:

In order to dynamically fill your second select box with data depending on the first, you need JavaScript.

In the following example, an event listener is registered for the first input field, which responds to events of type change. If the selection changes within this field, the required data is obtained asynchronously from a second endpoint and inserted into the second input field. The Fetch API is used for this, which receives the data in JSON format.

The ticker symbols will be sent in a comma separated string via GET request. This means that these are transferred as URL parameters, i.e. appended to the URL. With the query request.arg.get(...) the values are queried based on their name. The default is an empty list. In the lambda expression, the string is separated by commas and empty values are filtered out by a loop. You will get a list of ticker symbols.

symbols = request.args.get(
    'symbols',                                    # Name of parameter
    [],                                           # Default return value
    type=lambda x: [y for y in x.split(',') if y] # Split by ',' and filter empty strings
)

All rows whose "Symbol" column is contained in the list are then filtered out of the DataFrame. The "Open Price" column is extracted from these rows and any duplicates are prevented by a set.

open_prices = set(df[df['Symbol'].isin(symbols)]['Open Price'].tolist())

Finally, the received prices are returned as a list in JSON format, contained within a nested structure called "items".

return jsonify(items=list(open_prices))

When the form is finally sent, the data required for display is filtered from the DataFrame and passed to the template.

from flask import (
    Flask, 
    jsonify, 
    render_template, 
    request
)
import pandas as pd

df = pd.DataFrame(
    [
        ['Company1', 23, 10000],
        ['Company2', 22, 40000],
        ['Company2', 40, 40000], 
    ],
    columns=['Symbol','Open Price', 'Volume']
)

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
    symbols = set(df['Symbol'].tolist())
    open_prices = set(df['Open Price'].tolist())
    
    sel_symbols = symbols
    sel_open_prices = open_prices

    if request.method == 'POST':
        sel_symbols = request.form.getlist('symbols')
        sel_open_prices = request.form.getlist('open-prices', type=int)
        open_prices = set(df[df['Symbol'].isin(sel_symbols)]['Open Price'].tolist())
        
    data = df[df['Symbol'].isin(sel_symbols)][df['Open Price'].isin(sel_open_prices)]
    total_volumes = sum(data['Volume'])

    return render_template('index.html', **locals())

@app.route('/open-prices')
def open_prices(): 
    symbols = request.args.get('symbols', [], type=lambda x: [y for y in x.split(',') if y])
    open_prices = set(df[df['Symbol'].isin(symbols)]['Open Price'].tolist())
    return jsonify(items=list(open_prices))
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Index</title>
    <style type="text/css">
        table, th, td {
            border: 1px solid #9a9a9a; 
            border-collapse: collapse;
            padding: .64rem;
        }
        th {
            border-bottom: 2px solid #9a9a9a;
        }
        select {
            width: 75px;
        }
    </style>
</head>
<body>

    <form method="POST">
        <label for="symbols">Symbol</label>
        <select name="symbols" id="symbols" multiple>
            {% for symbol in symbols -%}
            <option value="{{ symbol }}" {% if symbol in sel_symbols %}selected{% endif %}>{{ symbol }}</option>
            {% endfor -%}
        </select>
        <label for="open-prices">Open Price</label>
        <select name="open-prices" id="open-prices" multiple>
            {% for price in open_prices -%}
            <option value="{{ price }}" {% if price in sel_open_prices %}selected{% endif %}>{{ price }}</option>
            {% endfor -%}
        </select>
        <button type="submit">Submit</button>
    </form>

    <hr />

    {% if data.values | count -%}
    <h4>Total Volume is {{total_volumes}} and selected 
        {{ 'are' if sel_open_prices | count > 1 else 'is'}} 
        {{ sel_open_prices | join(', ') }}.
    </h4> 

    <table width="100%">
        <thead>
            <tr>
                {% for column in data.columns %}
                <th>{{ column }}</th>
                {% endfor %}
            </tr>
        </thead>
        <tbody>
            {% for row in data.values %}
            <tr style="text-align:center">
                {% for cell in row %}
                <td>{{ cell }}</td>
                {% endfor %}
            </tr>
            {% endfor %}
        </tbody>
    </table>

    {% else -%}
        <p>No items found.</p>
    {% endif -%}

    <script type="text/javascript">
        (function(url) {

            const priceSelect = document.querySelector('select[name="open-prices"]');
            const tickerSelect = document.querySelector('select[name="symbols"]');
            tickerSelect && tickerSelect.addEventListener('change', function(event) {
                
                // Get selected items.
                let optionsSelected = [];
                let options = event.target.options;
                for (let opt of options) {
                    if (opt.selected) {
                        optionsSelected.push(opt.value || opt.text);
                    }
                }

                // Send AJAX request to the server.
                fetch(`${url}?symbols=${encodeURIComponent(optionsSelected.join(','))}`)
                    .then(resp => resp.ok && resp.json())
                    .then(data => {
                        // Add the entries to the input field.
                        priceSelect && (priceSelect.innerHTML = data.items.map(item => {
                            return `<option value="${item}" selected>${item}</option>`;
                        }).join(''))
                    });

            });

        })({{ url_for('open_prices') | tojson }});

    </script>

</body>
</html>
Answered By: Detlef
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.