2

I am setting up an restplus API, and want some default fields to be passed if the query don't contain these values. How do i pass these using an api model, preferably without the use of requestParser?

As it stands now, the payload doesn't get affected by setting default values, thus making it unnecessary. I have tried making the arguments required, but that is of no use since i want to be able to pass only parts of the expected payload.

from flask import Flask, request
from flask_restplus import Api, Resource, fields

app = Flask(__name__)
api = Api(app=app)
model = api.model("simple_model", {'some_bool': fields.Boolean(required=False, default=False),
                                   'some_int': fields.Integer(required=False, default=99)})


@api.route('/foo')
class SomeClass(Resource):

    @api.expect(model)
    def post(self):
        return request.json


if __name__ == '__main__':
    app.run(host='localhost', port=8000, threaded=False, debug=True)

Testing with the code

import requests

query = {"some_bool": True, "some_int": 20}
res = requests.post("http://localhost:8000/foo", json=query)
print(res.json())

query = {"some_bool": True}
res = requests.post("http://localhost:8000/foo", json=query)
print(res.json())

query = {"some_int": 20}
res = requests.post("http://localhost:8000/foo", json=query)
print(res.json())

res = requests.post("http://localhost:8000/foo")
print(res.json())

This gives the output

{'some_bool': True, 'some_int': 20}

{'some_bool': True}

{'some_int': 20}
None

The expected value, and the desired output is

{'some_bool': True, 'some_int': 20}

{'some_bool': True, 'some_int': 99}

{'some_bool': False, 'some_int': 20}

{'some_bool': False, 'some_int': 99}

All help is appreciated.

EDIT:

After the answer by @IMCoins, Iended up writing a function that made me able to acces the items inside the function like this

def get_content(api_model):
   @marshal_with(api_model)
   def get_request():
      return request.json
   return get_request()

and then just accessing the content inside as

content = get_content(model)
Community
  • 1
  • 1
kklocker
  • 136
  • 1
  • 10

3 Answers3

1

As far as I understand it, if you want to make sure you have all the keys you need at entry point, you need to use RequestParser(). api.expect() decorator is used to document the swagger.

However, if you want to make sure your request always returns a basic template (something like your model here), you can use the model you created into the decorator api.marshal_with().

For instance, in your examples here, just replace expect by marshal_with and it will include the missing values in the response.

IMCoins
  • 3,149
  • 1
  • 10
  • 25
  • api.marshal_with() appear to work as intended when i pass validate=False in "@api.expect". Thank you. If api.marshal_with() always return this template, wouldn't that also mean i always have all the keys as RequestParser() would provide? – kklocker Jul 17 '19 at 12:57
  • I believe you wish to use this data inside the route. For this, you need to use RequestParser. If you want to make sure your route will always return a basic template, you should use marshal_with; – IMCoins Jul 17 '19 at 13:09
  • I made a workaround using marshal_with that seems to work. Check out the edit of the original post – kklocker Jul 18 '19 at 07:08
1

I think without request Parser, it's not possible to get what you want. this default keyword is not actually the answer. because if you try to debug with Logs, it's only receiving which you're passing as query. but then api is not adding the default parameter in missing case, the Required param doesn't work there too. so i guess use it as described above by @IMCoins ?

Deep Bhatt
  • 313
  • 1
  • 11
  • I suppose so, but the [docs](https://flask-restful.readthedocs.io/en/latest/reqparse.html) suggests other libraries such as [marshmallow](https://marshmallow.readthedocs.io/en/latest/). Ideally i would just want a function that took my models as input and got the desired output as RequestParser, but marshal_with() seems to work for now. Thank you – kklocker Jul 17 '19 at 13:20
-1

jsonschema actually has a solution for this... but dose not include it in there main library:

https://python-jsonschema.readthedocs.io/en/stable/faq/#why-doesn-t-my-schema-s-default-property-set-the-default-on-my-instance

schema = {'some_bool' {'type': 'bool', 'default': 'false'}, 'some_int': {'type': 'number', 'default': 99}}

def post()
   data = request.json
   DefaultValidatingDraft7Validator(schema).validate(data)
   return data

Should result in

res = requests.post("http://localhost:8000/foo")
print(res.json())
{'some_bool': False, 'some_int': 99}

david
  • 3
  • 2