flask-restx как создать парсер из модели

#python #flask #flask-restx

Вопрос:

Я использую flask-restx для документирования и форматирования своего api

У меня есть приложение, в том числе каталог, содержащий схемы json в следующем формате: http://json-schema.org/draft-07/schema#

(в моем приложении они сохранены в файле json, в приведенном ниже примере я поставил это как дикт, жестко закодированный, чтобы упростить пример)

используя схему, я хотел бы достичь 3 целей:

  1. Документация
  2. проверка по запросу
  3. анализ запроса

используя @api.expect(request_model, validate=True) , мне удалось достичь (1) и (2), но я не нахожу способа анализа с использованием существующей схемы, мне пришлось создать объект синтаксического reqparse.RequestParser() анализа и переписать параметры.
Есть ли способ создать модель на RequestParser основе модели? (модель создается из существующего файла схемы json)

вот мой код:

 from flask_restx import Api, inputs

api = Api(app, doc='/my_doc_path')


request_schema = {
    'type': 'object',
    'properties': {
        'param1': {'type': 'string'},
        'the_date': {"type": "string", "format": "date-time"},
    },
    'required': ['param1'],
}
request_model = api.schema_model('my_api_model', request_schema)

@api.route('/my_api/<string:id>')
class MyApi(Resource):


    @api.expect(request_model, validate=True)
    def post(self, id):
        """
        my cool app
        """
        parser = reqparse.RequestParser()
        parser.add_argument('param1', help='some param 1')
        parser.add_argument('the_date', type=inputs.datetime_from_iso8601, help='some date_time')
        args = parser.parse_args()
        do_something(args['param1'], args['the_date'])
 

есть ли способ сделать что-то вроде:

 parser = reqparse.RequestParser(request_model)
args = parser.parse_args()
 

?

Ответ №1:

Я закончил тем, что написал код для создания синтаксического анализатора, формирующего схему:

 def build_parser(schema):
    parser = RequestParser()

    for prop, conf in schema['properties'].items():
        if conf.get('type') == 'string' and conf.get('format') == 'date-time':
            parser.add_argument(prop, type=inputs.datetime_from_iso8601)
        elif conf['type'] == 'integer':
            parser.add_argument(prop, type=int)
        elif conf['type'] == 'boolean':
            parser.add_argument(prop, type=bool, default=False)
        elif conf['type'] == 'array':
            parser.add_argument(prop, default=list, action='append')
        else:
            parser.add_argument(prop)
     return parser
 

Я создал следующий класс декоратора:

 import functools
import inspect

class Rest:

    @staticmethod
    def route(api, route_str='', decorators=None):
        decorators = decorators or []

        def wrapper(cls):
            for name, method in inspect.getmembers(cls, inspect.isfunction):
                schema_name = getattr(method, '_schema_name', None)
                if schema_name:
                    schema = getattr(method, '_schema', None)
                    setattr(cls, name, api.expect(api.schema_model(schema_name, schema), validate=True)(method))

            cls.method_decorators = cls.method_decorators   decorators
            return api.route(route_str)(cls) if route_str else cls

        return wrapper

    @staticmethod
    def schema(schema_name, parse=False):
        def decorator(f):
            f._schema_name = schema_name
            f._schema = get_api_schema(schema_name) # this function reads the json file
            parser = build_parser(f._schema)

            @functools.wraps(f)
            def wrapper(*args, **kwargs):
                if parse:
                    req_args = parser.parse_args()
                    return f(*args, req_args, **kwargs)

                return f(*args, **kwargs)

            return wrapper

        return decorator
 

и вот как я его использую:

 @Rest.route(api, '/my_api/<string:id>')
class MyApi(Resource):


    @Rest.route('my_api_schema_name', validate=True)
    def post(self, args, id):
        do_something(args['param1'], args['the_date'])
 

обратите внимание, что декоратор, который я написал, проанализирует аргументы и передаст их post функции