Reputation: 611
I initially had one long Flask form on a single web page, but now I want to break up that form into smaller chunks and iterate through those chunks with pagination buttons. I'm just a bit confused on how to approach this.
So far, I've created separate form classes and html templates for these different "chunks", but I'm unsure how to tie it all together in the main app.py script. I was going for a dynamic routing approach, but I don't think I'm doing it correctly. Any suggestions?
Form class:
from flask_wtf import FlaskForm
from wtforms import StringField, TextField, SubmitField, IntegerField, SelectField, validators
class InputForm1(FlaskForm):
stack_name = StringField('STACK NAME', validators=[validators.required()])
resource_cfn_tmpl_deploy_bucket = SelectField('PIPELINE 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")])
class InputForm2(FlaskForm):
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()])
class InputForm3(FlaskForm):
target = StringField('TARGET')
package_name = StringField('PACKAGE NAME', validators=[validators.required()])
license_name = StringField('LICENSE NAME', validators=[validators.required()])
class InputForm4(FlaskForm):
cohort_prefix = StringField('COHORT PREFIX', validators=[validators.required()])
build = SelectField('BUILD', validators=[validators.required()], choices=[("", "---"), ("","GRCh38"), ("","GRCh37")])
ome = SelectField('OME', validators=[validators.required()], choices=[("", "---"), ("", "wgs"), ("", "wes")])
class InputForm5(FlaskForm):
cloudspan_mode = SelectField('CLOUDSPAN MODE', choices=[("", "---"), ("", "validation"), ("", "qc")])
project_id = StringField('PROJECT ID')
zone = StringField('ZONE')
cloud_file = StringField('CLOUD FILE')
submit_final = SubmitField('Execute Pipeline')
pipeline_1.html: (I also have pipeline_2.....pipeline_5.html, with the different inputs as defined in the above Form class)
{% extends 'layout.html' %}
{% block body %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<br><br><br>
<legend class="border-bottom mb-4">Pipeline Input</legend>
<div class="form-group required">
{{ form.stack_name.label(class="form-control-label")}}
{{ form.stack_name(class="form-control form-control-lg") }}
</div>
<div class="form-group required">
{{form.resource_cfn_tmpl_deploy_bucket.label(class="form-control-label") }}
form.resource_cfn_tmpl_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>
<nav aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item"><a class="page-link" href=href="#" tabindex="-1">Previous</a></li>
<li class="page-item"><a class="page-link" href="{{ url_for('/1') }}">1</a></li>
<li class="page-item"><a class="page-link" href="{{ url_for('/2') }}">2</a></li>
<li class="page-item"><a class="page-link" href="{{ url_for('/3') }}">3</a></li>
<li class="page-item"><a class="page-link" href="{{ url_for('/4') }}">4</a></li>
<li class="page-item"><a class="page-link" href="{{ url_for('/5') }}">5</a></li>
<li class="page-item"><a class="page-link" href="#">Next</a></li>
</ul>
</nav>
</div>
</fieldset>
</div class="form-group">
</div>
</form>
<p>{{ error }}</p>
</div>
{% endblock %}
app.py: (current attempt, I know this is incorrect, but this is where I'm stuck)
# Import the Flask app
from flask import Flask, url_for, render_template, request, flash, redirect
from math import ceil
from datetime import datetime
from user_input import InputForm1, InputForm2, InputForm3, InputForm4, InputForm5
import json
import subprocess
import os
import sys
import boto3
app = Flask('pipeline-ui')
app.config['S3_CLIENT'] = boto3.client('s3')
app.config['EC2_CLIENT'] = boto3.client('ec2')
app.debug = True
@app.route('/<page_num>', methods=['GET', 'POST'])
def pipeline(page_num):
if request.method == "POST":
if page_num == 1:
form = InputForm1(request.form)
return redirect('/2')
if page_num == 2:
form = InputForm2(request.form)
return redirect('/3')
if page_num == 3:
form = InputForm3(request.form)
return redirect('/4')
if page_num == 4:
form = InputForm4(request.form)
return redirect('/5')
if page_num == 5:
form = InputForm5(request.form)
return redirect('results')
return render_template('pipeline_{}.html'.format(page_num), title='Pipeline Input', form=form)
Upvotes: 0
Views: 601
Reputation: 2887
Remember that there's no state between requests so, everything else correct, you need a way to store the data of each step for the next one.
If you can't store the data of each step in a database, you might consider reordering the form so that each step could make sense as a commit to the database.
If you don't use a database (or a file) or you can't send the data until all the steps are complete, then another option is just storing the data of each step into the next one, in hidden inputs.
class StepForm(FlaskForm):
previous_field = SomeField(widget=HiddenInput())
# ...
step = StepForm()
step.previous_field.data = previous_step.field.data
Of course, you could also just paginate in the frontend using javascript and sending all the data entirely in a single request at the end.
Upvotes: 0