pythonflaskquart

Save values from POST request of a list of dicts


I a trying to expose an API (if that's the correct way to say it). I am using Quart, a python library made out of Flask and this is what my code looks like:

async def capture_post_request(request_json):
    for item in request_json:
        callbackidd = item['callbackid']
        print(callbackidd)

@app.route('/start_work/', methods=['POST'])
async def start_work():
    content_type = request.headers.get('content-type')
    if (content_type == 'application/json'):
        request_json = await request.get_json()
        loop = asyncio.get_event_loop()
        loop.create_task(capture_post_request(request_json)) 
        body = "Async Job Started"
        return body
    else:
        return 'Content-Type not supported!'

My schema looks like that:

[
    {
        "callbackid": "dd",
        "itemid": "234r",
        "input": [
            {
                "type": "thistype",
                "uri": "www.uri.com"
            }
        ],
        "destination": {
            "type": "thattype",
            "uri": "www.urino2.com"
        }
    },
    {
        "statusCode": "202"
    }
]

So far what I am getting is this error:

line 11, in capture_post_request
    callbackidd = item['callbackid']
KeyError: 'callbackid'

I've tried so many stackoverflow posts to see how to iterate through my list of dicts but nothing worked. At one point in my start_work function I was using the get_data(as_text=True) method but still no results. In fact with the last method (or attr) I got:

TypeError: string indices must be integers

Any help on how to access those values is greatly appreciated. Cheers.


Solution

  • Your schema indicates there are two items in the request_json. The first indeed has the callbackid, the 2nd only has statusCode.

    Debugging this should be easy:

    async def capture_post_request(request_json):
        for item in request_json:
            print(item)
            callbackidd = item.get('callbackid')
            print(callbackidd) # will be None in case of the 2nd 'item'
    

    This will print two dicts:

    {
        "callbackid": "dd",
        "itemid": "234r",
        "input": [
            {
                "type": "thistype",
                "uri": "www.uri.com"
            }
        ],
        "destination": {
            "type": "thattype",
            "uri": "www.urino2.com"
        }
    }
    

    And the 2nd, the cause of your KeyError:

    {
        "statusCode": "202"
    }
    

    I included the 'fix' of sorts already:

    callbackidd = item.get('callbackid')
    

    This will default to None if the key isn't in the dict.

    Hopefully this will get you further!

    Edit

    How to work with only the dict containing your key? There are two options.

    First, using filter. Something like this:

    def has_callbackid(dict_to_test):
        return 'callbackid' in dict_to_test
    
    list_with_only_list_callbackid_items = list(filter(has_callbackid, request_json))
    # Still a list at this point! With dicts which have the `callbackid` key
    

    Filter accepts some arguments:

    1. Function to call to determine if the value being tested should be filtered out or not.
    2. The iterable you want to filter

    Could also use a 'lambda function', but it's a bit evil. But serves the purpose just as well:

    list_with_only_list_callbackid_items = list(filter(lambda x: 'callbackid' in x, request_json))
    # Still a list at this point! With dict(s) which have the `callbackid` key
    

    Option 2, simply loop over the result and only grab the one you want to use.

    found_item = None # default
    for item in request_json:
        if 'callbackid' in item:
            found_item = item
            break # found what we're looking for, stop now
    # Do stuff with the found_item from this point.