jpanknin
jpanknin

Reputation: 115

Flask return redirect(url_for) error for missing parameter

I have the following code and when I try a return redirect(url_for) on the present and future functions I get the following error message:

TypeError: home() missing 1 required positional argument: 'calc_type'

I've tried multiple ways to pass the calc_type variable to the home function and always get the same error. Any help is appreciated.

from flask import render_template, redirect, url_for, request
from app import app, db
from models import Pres, Future

@app.route('/home')
def home(calc_type):
    if (calc_type == "present"):
        try:
            inputs = Pres.query.all()
            for item in inputs:
                cash = item.cash
                rate = item.rate
                periods = item.periods
            pv = cash / (1 + rate)**periods
            return render_template('present.html', inputs=inputs, cash=cash, pv=pv)
        except:
            return render_template('present.html')
    elif (calc_type == "future"):
        inputs = Future.query.all()
        for item in inputs:
            cash = item.cash
            rate = item.rate
            periods = item.periods
        fv = cash * (1 + rate)**periods
        return render_template('present.html', inputs=inputs, cash=cash, fv=fv)

@app.route('/present', methods=['GET', 'POST'])
def present():
    input = Pres(request.form['cash'], request.form['rate'], request.form['periods'])
    calc_type = "present"
    db.session.add(input)
    db.session.commit()
    return redirect(url_for('home', calc_type=calc_type))

@app.route('/future', methods=['GET', 'POST'])
def future():
    input = Future(request.form['cash'], request.form['rate'], request.form['periods'])
    calc_type = "future"
    db.session.add(input)
    db.session.commit()
    return redirect(url_for('home', calc_type=calc_type))

Upvotes: 0

Views: 5283

Answers (1)

gtalarico
gtalarico

Reputation: 4689

The way you have your home() view, there is no way for it to receive a parameter from url_to because there is no such a variable in the route's path.

View functions are wrapped by flask and will behave differently than regular functions. For the home view to receive the variable you would have to define it with a variable in it's route path:

@app.route('/home/<string:calc_type>')
def home(calc_type):
   ...

# Result:
>>> url_for('route', calc_type='present')
'/home/present'

Alternatively, you can pass it as url query variables:

@app.route('/home')
def home():
   calc_type = request.args.get('calc_type')

# Result:
>>> url_for('route', calc_type='present')
'/home?calc_type=present'

But most importantly, you should also try to separate view/routing logic from other code and keep your views as light as possible.

Here are two ways you could solve this:

Option 1

PS: I would not do it this way, but is the closest to your code as is.

def get_view(calc_type):
    if (calc_type == "present"):
        try:
            inputs = Pres.query.all()
            for item in inputs:
                cash = item.cash
                rate = item.rate
                periods = item.periods
            pv = cash / (1 + rate)**periods
            return render_template('present.html', inputs=inputs, cash=cash, pv=pv)
        except:
            return render_template('present.html')
    elif (calc_type == "future"):
        inputs = Future.query.all()
        for item in inputs:
            cash = item.cash
            rate = item.rate
            periods = item.periods
        fv = cash * (1 + rate)**periods
        return render_template('present.html', inputs=inputs, cash=cash, fv=fv)


@app.route('/present', methods=['GET', 'POST'])
def present():
    input = Pres(request.form['cash'], request.form['rate'], request.form['periods'])
    calc_type = "present"
    db.session.add(input)
    db.session.commit()
    return get_view(calc_type)

@app.route('/future', methods=['GET', 'POST'])
def future():
    input = Future(request.form['cash'], request.form['rate'], request.form['periods'])
    calc_type = "future"
    db.session.add(input)
    db.session.commit()
    return get_view(calc_type)

Option 2

# calculations.py
from models import Pres, Future

def present_calc():
    try:
        inputs = Pres.query.all()
        for item in inputs:
            cash = item.cash
            rate = item.rate
            periods = item.periods
        pv = cash / (1 + rate)**periods
        return dict(inputs=inputs, cash=cash, pv=pv)
    except:
        return dict()

def future_calc():
    inputs = Future.query.all()
    for item in inputs:
        cash = item.cash
        rate = item.rate
        periods = item.periods
    fv = cash * (1 + rate)**periods
    return dict(inputs=inputs, cash=cash, fv=fv)


# views.py
from flask import render_template, redirect, url_for, request
from app import app, db

from .calculations import present_calc, future_calc
from app import db



@app.route('/present', methods=['GET', 'POST'])
def present():
    inputs = Pres(
        cash=request.form['cash'],
        rate=request.form['rate'],
        periods=request.form['rate'])
    db.session.add(inputs)
    db.session.commit()

    calcs = present_calc()
    return render_template('present.html', **calcs)

@app.route('/future', methods=['GET', 'POST'])
def future():
    inputs = Pres(
        cash=request.form['cash'],
        rate=request.form['rate'],
        periods=request.form['rate'])
    db.session.add(inputs)
    db.session.commit()

    calcs = future_calc()
    return render_template('future.html', **calcs)

Upvotes: 1

Related Questions