Search code examples
pythonflaskflask-wtformspeewee

Repeat flask form in loop based on other form's value


I am trying to use a value (n) from a form (ProjectForm) to generate a second form (StepForm) n times in a loop. The ProjectForm works, creates a project and then sends me to the StepForm, but on submit the StepForm does not update and the loop never stops.

@app.route("/new_project", methods=("POST", "GET"))
@login_required
def post_project():
    form = forms.ProjectForm()
    if form.validate_on_submit():
        models.Project.create(user=g.user.id,
                              title=form.title.data,
                              steps_number = form.steps_number.data,
                              description=form.description.data.strip())
        flash("Project created", "success")
        if form.steps_number.data != 0:
            project = models.Project.select().where(models.Project.title == form.title.data).get()
            return redirect(url_for("add_step",project_id=project.id))
        if form.steps_number.data == 0:
            return redirect(url_for("projects"))
    return render_template("post_project.html", form=form)

@app.route("/new_step/<int:project_id>", methods=("POST", "GET"))
@login_required
def add_step(project_id):
    form = forms.StepForm()
    project = models.Project.select().where(models.Project.id == project_id).get()
    steps = 0
    for i in range(0,project.steps_number):
        steps+=1
        if form.validate_on_submit():
            models.Step.create(project=project.id,
                                    title=form.title.data.strip(),
                                    instruction=form.instruction.data.strip())
            flash("Step Added", "success")
            if project.steps_number == steps:
                return redirect(url_for("projects"))
        return render_template("add_step.html", form=form, step=steps)

SOLVED see answer bellow

@app.route("/new_project", methods=("POST", "GET"))
@login_required
def post_project():
    form = forms.ProjectForm()
    if form.validate_on_submit():
        models.Project.create(user=g.user.id,
                              title=form.title.data,
                              steps_number = form.steps_number.data,
                              description=form.description.data.strip())
        flash("Project created", "success")
        if form.steps_number.data != 0:
            project = models.Project.select().where(models.Project.title == form.title.data).get()
            return redirect(url_for("add_step",project_id=project.id,step_id=1))
        if form.steps_number.data == 0:
            return redirect(url_for("projects"))
    return render_template("post_project.html", form=form)

@app.route("/new_step/<int:project_id>/<int:step_id>", methods=("POST", "GET"))
@login_required
def add_step(project_id,step_id):
    project = models.Project.select().where(models.Project.id == project_id).get()
    form = forms.StepForm()
    step = step_id
    if form.validate_on_submit():
        models.Step.create(project=project.id,
                            title=form.title.data.strip(),
                            instruction=form.instruction.data.strip())
        flash("Step Added", "success")
        step +=1
        return redirect(url_for("add_step",project_id=project.id,step_id=step))
    if project.steps_number == step:
                return redirect(url_for("projects"))
    return render_template("add_step.html", form=form, step_id=step)

Solution

  • You cannot have return inside a loop and expect the loop the work

    def f():
        for i in range(100):
            return i
    
    f()
    # >>> 0
    

    Your page and route need to by reactive, so pass the current the step number to the page as a render argument: render_template("add_step.html", form=form, step=steps) and then pass back that number to the add step route and an input argument and remove your inner loop that doesn't work:

    @app.route("/new_step/<int:project_id>/<int:step>", methods=("POST", "GET"))
    @login_required
    def add_step(project_id, step):
    

    You will need to update your form action html link to include the step