Search code examples
pythonapiserverpostmankeyerror

KeyError on POST method in simple Flask python backend


I'm trying to create a simple API and server with MongoDB and Flask in python(pycharm). I'm testing the methods with Postman and so far the GET and DELETE methods work. I'm having troubles with the POST method mainly(for adding an entity). I have 2 classes

repository class

class ExercisesRepository:

    def __init__(self):
        self.client = MongoClient('localhost', 27017)
        self.client.drop_database("exercise_database")  # clear everything that was before
        self.db = self.client.exercise_database  # create database
        self.exercises = self.db.exercises  # create table in the database

    def get_all(self):
        return [{
            'id': str(exercise['_id']),
            'type': exercise['type'],
            'calories': exercise['calories']
        } for exercise in self.exercises.find()]

    def add(self, exercise):
        exercise = {key: exercise[key] for key in exercise}
        exercise['calories'] = int(exercise['calories'])  #line 24
        self.exercises.insert_one(exercise)  # automatically generates an ObjectId for the exercise
        return 200

    def update(self, exercise_id, exercise):
        my_query = {"_id": ObjectId(exercise_id)}
        new_values = {"$set": {"type": exercise["type"], "calories": exercise["calories"]}}
        self.exercises.update_one(my_query, new_values)
        return 200

    def delete(self, exercise_id):
        self.exercises.remove(ObjectId(exercise_id))
        return 200

    def check_database_content(self):
        for exercise in self.exercises.find():
            pprint.pprint(exercise)

server class

from ExercisesRepository import ExercisesRepository

from flask import Flask
from flask import request
from flask import jsonify
import sys


app = Flask(__name__)

exerciseRepo = ExercisesRepository()

exerciseRepo.add({'type': 'Yoga', 'calories': 500})
exerciseRepo.add({'type': 'Walk', 'calories': 300})
exerciseRepo.add({'type': 'Run', 'calories': 100})


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route("/exercises", methods=['GET', 'POST'])
def exercises():
    if request.method == 'GET':
        return jsonify(exerciseRepo.get_all())
    elif request.method == 'POST':
        print(request.form, file=sys.stderr)
        return jsonify(exerciseRepo.add(request.form))  #line 31


@app.route('/exercises/<exercise_id>', methods=['PUT', 'DELETE'])
def exercises_id(exercise_id):
    if request.method == 'PUT':
        print(request.form, file=sys.stderr)
        return jsonify(exerciseRepo.update(exercise_id, request.form))
    elif request.method == 'DELETE':
        return jsonify(exerciseRepo.delete(exercise_id))


if __name__ == '__main__':
    app.run()

When I try to make a POST call in postman with a JSON like this : { "type": "Aerobic", "calories": 500 } I get the following message in postman: 500 Internal Server Error Internal Server Error The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application. and in Pycharm console:

File "server.py", line 31, in exercises return jsonify(exerciseRepo.add(request.form))
server\ExercisesRepository.py", line 24, in add exercise['calories'] = int(exercise['calories']) KeyError: 'calories' 127.0.0.1 - - [05/Jan/2020 13:01:50] "POST /exercises HTTP/1.1" 500 -

I'm pretty new to python and this is my first try to make an api so if you could explain as much as possible it would be very helpful. Thanks!


Solution

  • If you send data as JSON then you have to get it using request.json, not request.form

    from flask import Flask, request, jsonify
    
    app = Flask(__name__)
    
    @app.route("/exercises", methods=['GET', 'POST'])
    def exercises():
        print('form:', request.form)
        print('json:', request.json)
        return jsonify(request.json)  #line 31
    
    if __name__ == '__main__':
        app.run()
    

    When you send as JSON

    import requests
    
    r = requests.post('http://localhost:5000/exercises', json={'type': 'Run', 'calories': 100})
    

    then server shows

    form: ImmutableMultiDict([])
    json: {'type': 'Run', 'calories': 100}
    

    When you send as form data

    import requests
    
    r = requests.post('http://localhost:5000/exercises', data={'type': 'Run', 'calories': 100})
    

    then server shows

    form: ImmutableMultiDict([('type', 'Run'), ('calories', '100')])
    json: None
    

    form sends data as string type=Run&calories=100 in body,
    json sends data as string {'type': 'Run', 'calories': 100} in body.

    You can see it if you display request's body

    import requests
    
    r = requests.post('https://httpbin.org/post', data={'type': 'Run', 'calories': 100})
    print(r.request.body)
    
    r = requests.post('https://httpbin.org/post', json={'type': 'Run', 'calories': 100})
    print(r.request.body)
    

    Result

    type=Run&calories=100
    b'{"type": "Run", "calories": 100}'