Home > database >  Create a FlaskForm dropdown based on the query string value
Create a FlaskForm dropdown based on the query string value

Time:04-11

class DownloadForm(FlaskForm):    
products = service.get_products()
        choices = [(product, product.replace('-', ' '))
                       for product in products]

application = SelectField(
        'Application',
        [DataRequired()],
        choices=choices

submit = SubmitField('Submit')




@home.route('/downloads/<application_name>', methods=['GET', 'POST'])
def downloads():
    download_form = DownloadForm()

My route will be downloads/application_name. Can I send this routevalue "application_name" to the form to filter out the choices in the Download Form? I'm new to entire flask and python.

CodePudding user response:

You can use a factory function to use the parameter from the URL to do your database query. Based on this query, a form is then created that can be used within the route.

def download_form_factory(name):
    products = [
        'Archiver',
        'Editor-1',
        'Editor-2', 
        'Music-Player', 
        'Video-Player'
    ]
    class DownloadForm(FlaskForm):
        app = SelectField(
            'Application',
            choices= [
                (product, product.replace('-', ' ')) \
                for product in filter(lambda p: name.lower() in p.lower(), products)
            ]
        )
        submit = SubmitField('Download')
    return DownloadForm

The following example uses an input field of type QuerySelectField from WTForms-SQLAlchemy to facilitate database queries. As you can see above, the functionality is also possible with a normal SelectField.

A number of products are added to the database which, in addition to their name, have an additional property build.
If the user selects a name on the index page, all products that have the selected name are available for selection. He can then choose between the offered versions.

The form is created inside a function. The name to be searched for is passed to this function as a parameter. This can then be used to create the database query.

def download_form_factory(name):
    # Create a class of the form.
    class DownloadForm(FlaskForm):
        # Create the select field
        app = QuerySelectField(
            # with a label
            'Application', 
            # and the database entries containing the given name as options
            query_factory=lambda: Product.query.filter(Product.name.ilike(f'%{name}%')).all(),
            # and a label for each of these options.
            get_label=lambda p: '%s (Build %s)' % (p.name, p.build),
            # An option must be selected by the user.
            validators=[InputRequired()]
        )
        # Create the Submit button.
        submit = SubmitField('Download')
    # Return the created form class.
    return DownloadForm

Within the endpoint, the function mentioned is used and an instance of the form is created, which can be used in the usual way.

@app.route('/download/<string:name>', methods=['GET', 'POST'])
def download(name):
    # Create the form and an instance of it.
    form = download_form_factory(name)(request.form)
    if form.validate_on_submit():
        # If the POST request is sent with valid information, 
        # the form data can be requested.
        # The result is the selected database object.
        print(form.app.data)
    return render_template('download.html', **locals())

Here is the complete example.

Flask (app.py)
from flask import (
  Flask,
  render_template,
  request,
)
from flask_wtf import FlaskForm
from flask_sqlalchemy import SQLAlchemy
from wtforms import SubmitField
from wtforms_sqlalchemy.fields import QuerySelectField
from wtforms.validators import InputRequired

app = Flask(__name__)
app.secret_key = b'your secret here'
db = SQLAlchemy(app)

class Product(db.Model):
    __table_args__ = (db.UniqueConstraint('name', 'build'),)
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)
    build = db.Column(db.String, nullable=False)

# Create a class of the form.
def download_form_factory(name):
    class DownloadForm(FlaskForm):
        app = QuerySelectField(
            'Application',
            get_label=lambda p: f'{p.name} (Build {p.build})',
            query_factory=lambda: Product.query.filter(Product.name.ilike(f'%{name}%')).all(),
            validators=[InputRequired()]
        )
        submit = SubmitField('Download')
    return DownloadForm

# Fill the database with sample data.
with app.app_context():
    db.drop_all()
    db.create_all()
    products = [Product(name=f'Product-{str(i).zfill(2)}', build=f'{j}') \
        for i in range(1, 21) for j in range(1,i 1)]
    db.session.add_all(products)
    db.session.commit()

@app.route('/')
def index():
    products = Product.query.group_by(Product.name).order_by(Product.name).all()
    return render_template('index.html', **locals())

@app.route('/download/<string:name>', methods=['GET', 'POST'])
def download(name):
    # Create the final object of the form.
    form = download_form_factory(name)(request.form)
    if form.validate_on_submit():
        # Inquire about the selected product.
        print(form.app.data)
    return render_template('download.html', **locals())
HTML (templates/index.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Index</title>
  </head>
  <body>
    <ul>
      {% for prod in products -%}
      <li><a href="{{ url_for('download', name=prod.name)}}">{{prod.name}}</a></li>
      {% endfor -%}
    </ul>
  </body>
</html>
HTML (templates/download.html)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Download</title>
  </head>
  <body>
    <form method="post">
      {{ form.csrf_token }}
      <div>
        {{ form.app.label }}
        {{ form.app() }}
      </div>
      {{ form.submit() }}
    </form>
  </body>
</html>
  • Related