Reputation: 611
Currently, using Flask, I have a home page with a single (long) form for users to fill out. However, I'd like to separate this form out into chunks and iterate through them with a "Next" button (kind of like when you buy something online and have to go through different pages to fill out your information). So far, I have tried the following:
Implementing multiple "Submit" buttons in the homepage html:
{% extends 'layout.html' %}
{% block body %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Pipeline Input</legend>
<div class="form-group required">
{{ form.stack_name.label(class="form-control-label") }}
{{ form.deploy_bucket(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.key_pair.label(class="form-control-label") }}
{{ form.key_pair(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.start_point.label(class="form-control-label") }}
{{ form.start_point(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.qc.label(class="form-control-label") }}
{{ form.qc(class="form-control form-control-lg") }}
</div>
{{ form.submit_1(class="btn btn-outline-info") }}
<div class="form-group required">
{{ form.input_uri.label(class="form-control-label") }}
{{ form.input_uri(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.output_uri.label(class="form-control-label") }}
{{ form.output_uri(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.ref_uri.label(class="form-control-label") }}
{{ form.ref_uri(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.user_assets_uri.label(class="form-control-label") }}
{{ form.user_assets_uri(class="form-control form-control-lg") }}
</div>
{{ form.submit_2(class="btn btn-outline-info") }}
<div class="form-group">
{{ form.target.label(class="form-control-label") }}
{{ form.target(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.package_name.label(class="form-control-label") }}
{{ form.package_name(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{ form.license_name.label(class="form-control-label") }}
{{ form.license_name(class="form-control form-control-lg") }}
</div>
{{ form.submit_final(class="btn btn-outline-info") }}
</div>
</fieldset>
</div class="form-group">
</div>
</form>
<p>{{ error }}</p>
</div>
{% endblock %}
Here is where the form inputs are defined:
from flask_wtf import FlaskForm
from wtforms import StringField, TextField, SubmitField, IntegerField, SelectField, validators
class InputForm(FlaskForm):
stack_name = StringField('STACK NAME', validators=[validators.required()])
deploy_bucket = SelectField('DEPLOYMENT BUCKET', validators=[validators.required()])
key_pair = SelectField('KEY PAIR', validators=[validators.required()])
start_point = SelectField('START POINT', validators=[validators.required()], choices=[("", "---"), ("", "fastq"), ("", "bam"), ("", "hdof"), ("", "gvcf"), ("", "vcf")])
qc = SelectField('QC', choices=[("", "---"), ("","BAM"), ("","VCF")])
submit_1 = SubmitField('Next')
input_uri = StringField('INPUT BUCKET', validators=[validators.required()])
output_uri = StringField('OUTPUT BUCKET', validators=[validators.required()])
ref_uri = SelectField('REFERENCE BUCKET', validators=[validators.required()])
user_assets_uri = StringField('USER ASSETS BUCKET', validators=[validators.required()])
submit_2 = SubmitField('Next')
target = StringField('TARGET')
package_name = StringField('PACKAGE NAME', validators=[validators.required()])
license_name = StringField('LICENSE NAME', validators=[validators.required()])
submit_final = SubmitField('Execute Pipeline')
Here is my app.py, where the form data is processed. It is currently still set up for just having one submit button, so I'm not sure how to handle the "next" buttons/pagination here:
@app.route('/', methods=['GET', 'POST'])
def pipeline():
form = InputForm(request.form)
form.deploy_bucket.choices = [("", "---")] + [("", bucket["Name"]) for bucket in app.config['S3_CLIENT'].list_buckets()["Buckets"]]
form.ref_uri.choices = [("", "---")] + [("", bucket["Name"]) for bucket in app.config['S3_CLIENT'].list_buckets()["Buckets"]]
form.key_pair.choices = [("", "---")] + [("", pair["KeyName"]) for pair in app.config['EC2_CLIENT'].describe_key_pairs()["KeyPairs"]]
if request.method == 'POST':
STACK_NAME = form.stack_name.data
DEPLOY_BUCKET = form.deploy_bucket.data
KEY_PAIR = form.key_pair.data
START_POINT = form.start_point.data
QC = form.qc.data
INPUT_URI = form.input_uri.data
OUTPUT_URI = form.output_uri.data
REF_URI = form.ref_uri.data
USER_ASSETS_URI = form.user_assets_uri.data
TARGET = form.target.data
PACKAGE_NAME = form.package_name.data
LICENSE_NAME = form.license_name.data
flash('The pipeline has been executed!', 'success')
dic = {
"STACK_NAME": STACK_NAME,
"DEPLOY_BUCKET": DEPLOY_BUCKET,
"KEY_PAIR": KEY_PAIR,
"START_POINT": START_POINT,
"QC": QC,
"INPUT_URI": INPUT_URI,
"OUTPUT_URI": OUTPUT_URI,
"REF_URI": REF_URI,
"USER_ASSETS_URI": USER_ASSETS_URI,
"LOCAL_ASSETS_DIR": LOCAL_ASSETS_DIR,
"SAMPLE_FILE": SAMPLE_FILE,
"TARGET": TARGET,
"PACKAGE_NAME": PACKAGE_NAME,
"LICENSE_NAME": LICENSE_NAME,
}
proc = subprocess.Popen('pwd', shell=True, stdout=subprocess.PIPE)
with open("input.json", 'w') as f:
json.dump(dic, f)
proc = subprocess.Popen('python pipeline.py -p', shell=True, stdout=subprocess.PIPE)
return redirect(url_for('dashboard'))
return render_template('pipeline.html', title='Pipeline Input', form=form)
@app.route('/dashboard')
def dashboard():
return render_template('dashboard.html')
Upvotes: 0
Views: 2321
Reputation: 1386
If you're insistent on using Flask for this task, the easiest thing to do would be to separate your form into multiple forms, maybe by category. After processing a form, your flask code would then redirect to the next form.
However, particularly in 2018, this is something that should probably be done client side rather than server side. The reason is that your problem here really isn't related to Flask, which is responsible for processing the form, but to the front-end which is responsible for displaying the form.
The advantages are many. For one, you could load your entire form at once, but chunk what is displayed at each time. This would allow you to keep your flask code simple by only having a single form to submit, but give you precision control over what is displayed to the user. Furthermore, you're reducing the number of round trips from the client to the server, which will provide better performance for your users.
If you're looking to avoid writing the font-end paging code, there are solutions out there that could you integrate into your form. I've personally used Bootstrap's Pagination feature with great success.
Upvotes: 1