Skip to content

@api.expect and @api.response decorators don't consistently handle documentation of pre-defined Model from string name #56

Open
@p-lucero

Description

@p-lucero

Code

I'm trying to organize my code so that model definitions are shared, in order to reduce clutter, and so that pure API logic ends up in its own files. Here's the structure that I've got right now.

In app.py (the entry point):

from api import api
from flask import Flask

app = Flask(__name__)
api.init_app(app)

In api/__init__.py:

from flask_restx import Api

from .demo import api as demo_ns
from .models import register_models

api = Api()
register_models(api)
api.add_namespace(demo_ns)

In api/models.py:

from flask_restx import fields

models = {
    'My Cool Model': {'my_field': fields.String(description='A cool field!')}
}


def register_models(api):
    for model_name, model_data in models.items():
        api.model(name=model_name, model=model_data)

In api/demo.py:

from flask import json
from flask_restx import Namespace, Resource, Model

api = Namespace('demo', description='Demo and testing endpoints')


@api.route('/hello')
class HelloWorld(Resource):
    @api.expect(Model('My Cool Model'), validate=True)
    @api.response(200, description='Success', model=Model('My Cool Model'))
    def post(self):
        return json.jsonify({'my_field': 'hello!'})

Repro Steps (if applicable)

  1. Run the above code.
  2. Note that the generated documentation does not have a definition for the data returned on a 200 response, but it does have a definition for the data expected in the request.
  3. Change the model parameter in the api.response decorator to [Model('My Cool Model')].
  4. Reload the documentation.
  5. Note that the generated documentation now specifies that a list of objects are returned on a 200 response.

Expected Behavior

Constructing a model object should work the same in both places; if constructing an already-defined model object works for the api.expect decorator, it should also work for the api.response decorator, and vice versa.

Actual Behavior

With the above code, the model will properly render in the documentation for the api.expect decorator, but it will not render for the api.response decorator. However, changing the model parameter in the api.response decorator from Model('My Cool Model') to [Model('My Cool Model')] (i.e. encapsulating it in a list) causes it to render. This is fine for when I want my defined API to produce a list of objects, but not so good for when I want my defined API to produce a single object.

Environment

  • Python version
    Running the python3.7-slim docker image, exact version is below:
Python 3.7.5 (default, Nov 15 2019, 02:40:28) 
[GCC 8.3.0] on linux
  • Flask version: 1.1.1
  • Flask-RESTX version: 0.1.1
  • Other installed Flask extensions: N/A

Additional Context

My current workaround is to include a make_model function in the api/models.py file:

def make_model(api, model_name):
    return api.model(model_name, models[model_name])

This function can then be imported and called in api/demo.py as follows, and produces the expected result in the documentation:

from .models import make_model
# snip
@api.response(200, description='Success', model=make_model(api, 'My Cool Model'))

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions