Search code examples
pythonpython-3.xpostflaskhttp-status-code-400

POST method results in Bad Request


EDIT 2: added the whole main-element in the HTML code

I'm building my own website with Python, HTML, CSS and Jinja. On this page I'm trying to build a function where users can post comments and reviews on the given recipe.

So, I'd like to post the following code but it gives this error message:

"?[1m?[31mPOST /recipes HTTP/1.1?[0m" 400 -

{% block main %}

    <div id="recipe_id" style="display: none;" name="recipe_id">
        {% for recipe in recipes %}
        {{ recipe.recipe_id }}
        {% endfor %}
    </div>

    <table class="table table-striped">

    {% for recipe in recipes %}

        <h1>{{ recipe.recipe_name }}</h1>
        <a href= {{ recipe.link_to_recipe }} ><h4>Link to recipe</h4></a>
        <h5>{{ recipe.category }}</h5>
        <h5>{{ recipe.review }}</h5>

    {% endfor %} 
    </table>

    <div>
        <textarea name="comments" id="comment" type="text" autofocus="autofocus" form="commentrating">Give your comment</textarea>
    </div>

    <h3>Rate the recipe!</h3>
    <form action="/recipes" method="post" id="commentrating">
        <fieldset>
            <div id="review">
                <select name="rating">
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                </select>
            </div>
            <br>
            <div>
                <input type="submit" id="button" value="Submit">
            </div>
        </fieldset>  
    </form>
    <br>
    <table class="table table-striped" id="comments">
        <thead>
            <tr>
                <th colspan="1">Posted by</th>
                <th colspan="3">Comment</th>
            </tr>
        </thead>
    {% for comment in comments %}
        <tr>
            <td colspan="1">{{ comment.user }}</td>
            <td colspan="3">{{ comment.comments }}</td>
        </tr>
    {% endfor %} 
    </table>
{% endblock %}

After three hours, I'm finally 'admitting' that I'm stuck on why it gives error 400 as it looks like all my other form actions. It shows 31mPOST while my other form action show a 37mPOST. Maybe this helps.

Anyone knows what I'm doing wrong and how I can fix this?

EDIT

I have also tried to find a bug in the python code, but cannot find the cause of the 400 error. As the problem is probably in the server-side code, here is the code:

@app.route("/recipes", methods=["POST"])
@login_required

def recipes():
    """Function where the comment and rating is added to the recipe."""

    if request.method == "POST":
        #get the comment and/or rating
        comment = request.form["comments"]
        rating = int(request.form["rating"])
        recipe = int(request.form["recipe_id"])

        db.execute("INSERT INTO comments (recipe_id, comments, user) \
        VALUES (:recipe_id, :comments, :user)", \
        recipe_id=recipe, comments=comment, user=session["user.id"])

        givenrating = db.execute("SELECT reviews, number_of_reviews FROM recipes WHERE \
        recipe_id=:recipe", recipe=recipe)

        # check if there already is given a rating
        if givenrating[0]["number_of_reviews"] == "None":
            db.execute("UPDATE recipes SET review=:rating, number_of_reviews=:numb \
            WHERE recipe_id=:recipe", recipe=recipe, rating=rating, numb=1)

            #load chosen recipe
            recipes = db.execute("SELECT * FROM recipes JOIN categories ON \
            recipes.category = categories.cat_id WHERE recipe_id=:recipe", recipe=recipe)

            #load comments of the recipe
            comments = db.execute("SELECT * FROM comments JOIN users on \
            comments.user = users.id WHERE recipe_id=:recipe", recipe=recipe)

            return render_template("recipe.html", recipes=recipes, comments=comments)

        else:
            number = int(givenrating[0]["number_of_reviews"])
            ratings = int(givenrating[0]["reviews"])

            # update existing rating
            fullrating = ratings * number
            newrating = fullrating + rating
            number += 1
            averagerating = newrating / number

            db.execute("UPDATE recipes SET review=:review, number_of_reviews=:newnumber \
            WHERE recipe_id=:recipe", review=averagerating, newnumber=number, recipe=recipe)                

            #load chosen recipe
            recipes = db.execute("SELECT * FROM recipes JOIN categories ON \
            recipes.category = categories.cat_id WHERE recipe_id=:recipe", recipe=recipe)

            #load comments of the recipe
            comments = db.execute("SELECT * FROM comments JOIN users on \
            comments.user = users.id WHERE recipe_id=:recipe", recipe=recipe)

            return render_template("recipe.html", recipes=recipes, comments=comments)

Solution

  • This error code is for Bad Request. you are trying to get three elements(comments, rating and recipe_id), but they are not present in the form, so flask throws a 400 error.

    what you could do is using hidden inputs to send that kind of information.

    <form action="/recipes" method="post" id="commentrating">
        <input type="hidden" name="recipe_id" value="{{ recipe_id }}">
        <textarea name="comments" id="comment" type="text" autofocus="autofocus" form="commentrating">Give your comment</textarea>
        <fieldset>
            <div id="review">
                <select name="rating">
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                </select>
            </div>
            <br>
            <div>
                <input type="submit" id="button" value="Submit">
            </div>
        </fieldset>  
    </form>
    

    but, you should design your code differently, so that the recipe_id is set based on the recipe you want to add comment's for.

    you can achieve that in numerous ways, but since that's not in the context of this question, i will leave that part alone.

    another way to do is, using a JS code(jquery post or AJAX) to post the data, and that way, you could have make a variable with the data you want, and then post it.