0

What I'm trying to do is, once the user submits all the results I want it to update the Fixture_prediction model according to my filters. Although what I get is 400 bad request. The log doesnt tell me enough to know whats going wrong. Any ideas?

I think its to do with the tuple data submitted through the form...

The form displays fine its just when I submit the form it goes straight to a bad request.

my error

Bad Request

The browser (or proxy) sent a request that this server could not understand.

What I have currently:

views

@app.route('/predictor/',methods=['GET','POST'])
@login_required
def predictions():
    user_id = g.user.id
    # retrieve predictions
    prediction= db.session.query(Fixture_prediction,\
                Fixture_prediction.fixture_id,Fixture.stage,\
                Fixture.home_team,Fixture_prediction.home_score,\
                Fixture_prediction.away_score,Fixture.away_team)\
                .outerjoin(Fixture,Fixture.id==Fixture_prediction.fixture_id)\
                .outerjoin(User,Fixture_prediction.user_id == User.id)\
                .filter(Fixture_prediction.fixture_id==Fixture.id)\
                .filter(Fixture_prediction.user_id==user_id).all()
    data = {'predictions': prediction}
    form = PredictionListForm(data=MultiDict(data))
    if request.method == 'POST':
        if form.validate() == False:
            flash('A score is missing, please fill in all predictions')
            render_template('predictor.html', form=form)
        else:
            #for pred in prediction:
            store=Fixture_prediction.query\
                            .filter_by(user_id=user_id)\
                            .filter_by(fixture_id=request.form['fixture_id'])\
                            .update({'home_score':request.form['home_score']\
                                    ,'away_score':request.form['away_score']})
            db.session.commit()
            flash('Prediction added')
            return redirect(url_for("predictions"))
    # display current predictions
    elif request.method == 'GET':
        return render_template('predictor.html', form=form)

template

{% extends "base.html" %}

{% block content %}

  <h1>Predictions</h1>
  <p></p>
  <p>Please make your predictions here</p>
  <form action='' method='post'>
    {{form.predictions()}}
    <p><input type="submit" value="Submit Predictions"></p>
   </form>

{% endblock %}

forms

class PredictionForm(WTForm):
    fixture_id = fields.IntegerField(validators=[validators.required()])
    stage = fields.TextField(validators=[validators.required()])
    home_team = fields.TextField(validators=[validators.required()])
    home_score = fields.IntegerField(validators=[validators.required()])
    away_score = fields.IntegerField(validators=[validators.required()])
    away_team = fields.TextField(validators=[validators.required()])

class PredictionListForm(WTForm):
    predictions = FieldList(FormField(PredictionForm))
Lorbat
  • 385
  • 2
  • 8
  • 27
  • You haven't included your `@app.route` code-- are you telling it to accept post requests? e.g. `@app.route('/eg/', methods=['post','get'])` – Doobeh Jun 09 '14 at 14:41
  • sorry forgot to add that here but its in my code – Lorbat Jun 09 '14 at 14:44

2 Answers2

2

The problem is that there is no field fixture_id in request.form. This results in a KeyError being raised by the underlying MultiDict, which is translated into a 400 by Flask.

The reason there is no fixture_id is because you are using the field enclosures FieldList and FormField both of which alter the names you provide to WTForms to avoid collisions.

The fix is to simply use the form instance that you have to access the data (as WTForms has already mapped it for you):

# in your else clause
for prediction in form.predictions:
    store = Fixture_prediction.query \
                              .filter_by(user_id=user_id) \
                              .filter_by(fixture_id=prediction.fixture_id.data)
    # etc. 
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
0

According to the flask docs, a 400 occurs when:

What happens if the key does not exist in the form attribute? In that case a special KeyError is raised. You can catch it like a standard KeyError but if you don’t do that, a HTTP 400 Bad Request error page is shown instead. So for many situations you don’t have to deal with that problem.

It sounds like the wtform is accessing a key that is not in the multidict, raising a keyerror. To test this, wrap the validate call with a try/except. (I think, I'd assume this is where the keyerror occurs). If you catch the exception, you'll have your answer.

jaime
  • 2,234
  • 1
  • 19
  • 22
  • Sorry I've got a 400 bad request but I assume its the same. My set up is a Flask, all im doing is redirect(url_for('predictions')) which should bring it back to the current view...so it might be something else? – Lorbat Jun 09 '14 at 13:59