Home > Mobile >  How do I implement dynamic dependent change of dropdown select options, using Python, Flask and SQLi
How do I implement dynamic dependent change of dropdown select options, using Python, Flask and SQLi

Time:12-27

Good day. I'm really new to programming (3 month's), and i'm trying to add last feature for my course final project. In my html page, i have 5 select containers, first container has data from tuple object in my app.py, and rest 4 options data is coming fom sqlite3 database query. I want this options to be dependent of each other, no matter what option will be selected first by user. To give you more of an idea what I am talking about, all options are tied to weapon type (this is a web-app for managing inventory for an online game), this "type" column is present in each table used in query, and represented as "1" for ranged weapons, "2" for melee weapons and "0" for both types. So how I understand it, I need to make a json request after one option is being selected, and return it with new query result back to html in order to process new data with javascript? But what next, when new option will be selected and reduce variants... i'm totally lost here, since my javascript experience is only with bootstrap (copy/paste)... I hope I explained everything clearly, and would really appreciate some example how to do it. Thank you for your time.

My python code:

@app.route("/add", methods=["GET", "POST"])
@login_required
def add():
    
    # assigning user_id to session
    user_id = session["user_id"]
    
    # this will be used for future dynamic change feature
    weapon_type = ("-1", "1")
    
    if request.method == "POST":
        
        # assigning user's input
        wtype = request.form.get("wtype")
        wname = request.form.get("wname")
        main_p = request.form.get("main_p")
        major_p = request.form.get("major_p")
        minor_p = request.form.get("minor_p")
        
        # check if input fields are blank or not valid
        if not wtype or wtype not in weapon_type:
            return apology("Invalid type")
        if not wname:
            return apology("Invalid type")
        if not main_p:
            return apology("Invalid type")

        # adding new weapon into users_weapons table
        db.execute("INSERT INTO user_weapons (user_id, weapon_id, main_id, major_id, minor_id) VALUES(?, ?, ?, ?, ?)", user_id, wname, main_p, major_p, minor_p)
        
        return redirect("/inventory")
    
    else:
        
        # adding options for select forms
        weapons = db.execute("SELECT id, name FROM weapons")
        mains = db.execute("SELECT id, name FROM mainp")
        majors = db.execute("SELECT id, description FROM majorp")
        minors = db.execute("SELECT id, description FROM minorp")
        
        return render_template("add.html", weapons=weapons, mains=mains, majors=majors, minors=minors)

My html code:

{% block main %}
    <form action="/add" method="post">
        <div >
            <select  name="wtype">
                <option disabled selected>Weapon Type</option>
                <option value="-1">Melee</option>
                <option value="1">Ranged</option>
            </select>
            <br>
            <br>
            <select  name="wname">
                <option disabled selected>Weapon Name</option>
                {% for weapon in weapons %}
                    <option value="{{ weapon["id"] }}"> {{ weapon["name"] }} </option>
                {% endfor %}
            </select>
            <br>
            <br>
            <select  name="main_p">
                <option disabled selected>Main Prefix *</option>
                {% for main in mains %}
                    <option value="{{ main["id"] }}"> {{ main["name"] }} </option>
                {% endfor %}
            </select>
            <br>
            <br>
            <select  name="major_p">
                <option disabled selected>Major Prefix **</option>
                {% for major in majors %}
                    <option value="{{ major["id"] }}"> {{ major["description"] }} </option>
                {% endfor %}
            </select>
            <br>
            <br>
            <select  name="minor_p">
                <option disabled selected>Minor Prefix ***</option>
                {% for minor in minors %}
                    <option value="{{ minor["id"] }}"> {{ minor["description"] }} </option>
                {% endfor %}
            </select>
        </div>
        <br>
        <button  type="submit">Add Item</button>
    </form>
{% endblock %}

CodePudding user response:

So with help of my friend (shout out to Scraelos! :), we were able to solve this problem using Jsonify feature of Flask, and Jquery. It's a little bit different approach than I wanted to implement at first, but it suites general purpose of my web-app better.

(by doing this, i was able to remove the need to populate select options through GET method of "/add" route)

python code:

@app.route("/options")
@login_required
def options():
    
    wtype = request.args.get('wtype')
    
    weapons = db.execute("SELECT id, name FROM weapons WHERE type = ?", wtype)
    mains = db.execute("SELECT id, name FROM mainp WHERE type = ? OR type = 0", wtype)
    majors = db.execute("SELECT id, description FROM majorp WHERE type = ? OR type = 0", wtype)
    minors = db.execute("SELECT id, description FROM minorp WHERE type = ? OR type = 0", wtype)
    
    return jsonify(weapons=weapons, mains=mains, majors=majors, minors=minors)

javascript code:

$(document).ready(function() {

      // first we hide opions 2-5 and button
      $('#wname').hide();
      $('#main_p').hide();
      $('#major_p').hide();
      $('#minor_p').hide();
      $('#submitbtn').hide();

      // when 1st available option is changed, we get JSON from "/options"
      $('#wtype').change(function(){

        $.getJSON('/options', {
          wtype: $('#wtype').val()
        
        // if request successful process data
        }).done(function(data) {
              
              // remeber selected options for later use
              mainp=$('#main_p').val();
              majorp=$('#major_p').val();
              minorp=$('#minor_p').val();
              
              // empty options for now...
              $('#wname').empty();
              $('#main_p').empty();
              $('#major_p').empty();
              $('#minor_p').empty();
              
              // appending placeholder options
              $('#wname').append($('<option disabled selected>Weapon Name</option>'));
              $('#main_p').append($('<option disabled selected>Main Prefix &#9734</option>'));
              $('#major_p').append($('<option disabled selected>Major Prefix &#9734&#9734</option>'));
              $('#minor_p').append($('<option disabled selected>Minor Prefix &#9734&#9734&#9734</option>'));              
              
              // appending real options available for chosen type trough itteration
              $.each(data.weapons, function(key, val) {
                $('#wname').append($('<option>').text(val.name).attr('value', val.id));
              });
              $.each(data.mains, function(key, val) {
                $('#main_p').append($('<option>').text(val.name).attr('value', val.id));
              });
              $.each(data.majors, function(key, val) {
                $('#major_p').append($('<option>').text(val.description).attr('value', val.id));
              });
              $.each(data.minors, function(key, val) {
                $('#minor_p').append($('<option>').text(val.description).attr('value', val.id));
              });
              
              // after all new options were added to selection, this "if" condition checks if previously saved option is in new options list
              if ($("#main_p option[value=" mainp "]").length > 0){
                // if true, select this option
                $('#main_p').val(mainp).change();
              }
              
              if ($("#major_p option[value=" majorp "]").length > 0){
                $('#major_p').val(majorp).change();
              }
              
              if ($("#minor_p option[value=" minorp "]").length > 0){
                $('#minor_p').val(minorp).change();
              }
              
              // make select options and button fadeIn from "hide"
              $('#wname').fadeIn();
              $('#main_p').fadeIn();
              $('#major_p').fadeIn();
              $('#minor_p').fadeIn();
              $('#submitbtn').fadeIn();
         })
      });
    });

and HTML after all lookes like this:

{% extends "layout.html" %}

{% block title %}
    Add
{% endblock %}

{% block main %}
    <form action="/add" method="post">
        <div >
            <select  name="wtype" id="wtype">
                <option disabled selected>Weapon Type</option>
                <option value="-1">Melee</option>
                <option value="1">Ranged</option>
            </select>
            <br>
            <br>
            <select  name="wname" id="wname">
                
        <!--    I was able to remove below syntax, by performing table select population through Jquery code in fetch.js
                
                <option disabled selected>Weapon Name</option>
                {% for weapon in weapons %}
                    <option value="{{ weapon["id"] }}"> {{ weapon["name"] }} </option>
                {% endfor %}
                
        -->
            </select>
            <br>
            <br>
            <select  name="main_p" id="main_p"></select>
            <br>
            <br>
            <select  name="major_p" id="major_p"></select>
            <br>
            <br>
            <select  name="minor_p" id="minor_p"></select>
        </div>
        <br>
        <button  type="submit" id="submitbtn">Add Item</button>
    </form>
    <script src="/static/fetch.js"></script>
{% endblock %}

Maybe someone will find this solution useful. Peace! :)

  • Related