Я пишу REST API, который будет хранить несколько сложных объектов в AWS DynamoDB, а затем по запросу извлекать их, выполнять вычисления на них и возвращать результат.Вот большой извлеченный, упрощенный, переименованный псевдокод.
class Widget:
def __init__(self, height, weight):
self.height = height
self.weight = weight
class Machine:
def __init__ (self, widgets):
self.widgets = widgets
def useful_method ():
return "something great"
class WidgetSchema (Schema):
height = fields.Decimal()
weight = fields.Decimal()
@post_load
def make_widget (self, data):
return Widget(*data)
class MachineSchema (Schema):
widgets = fields.List(fields.Nested(WidgetSchema))
def make_machine (self, data):
return Machine(*data)
app = Flask(__name__)
dynamodb = boto3.resource("dynamodb", ...)
@app.route("/machine/<uuid:machine_id>", methods=['POST'])
def create_machine(machine_id):
input_json = request.get_json()
validated_input = MachineSchema().load(input_json)
# NOTE: validated_input should be a Python dict which
# contains Decimals instead of floats, for storage in DynamoDB.
validate_input['id'] = machine_id
dynamodb.Table('machine').put_item(Item=validate_input)
return jsonify({"status", "success", error_message = ""})
@app.route("/machine/<uuid:machine_id>/compute", methods=['GET'])
def get_machine(machine_id):
result = dynamodb.Table('machine').get_item(Key=machine_id)
return jsonify(result['Item'])
@app.route("/machine/<uuid:machine_id>/compute", methods=['GET'])
def compute_machine(machine_id):
result = dynamodb.Table('machine').get_item(Key=machine_id)
validated_input = MachineSchema().load(result['Item'])
# NOTE: validated_input should be a Machine object
# which has made use of the post_load
return jsonify(validated_input.useful_method())
Проблема в том, что мне нужно, чтобы моя схема Marshmallow выполняла двойную функцию.Для начала, в функции create_machine мне нужна схема, чтобы пользователь, вызывающий мой REST API, передал мне правильно сформированный объект без лишних полей и отвечающий всем необходимым полям и т. Д. Мне нужно убедиться, что я не сохраняюнедействительный мусор в БД в конце концов.Также необходимо рекурсивно сканировать входной JSON и преобразовать все значения JSON в правильный тип.Например, числа с плавающей запятой не поддерживаются в Динамо, поэтому они должны быть десятичными, как показано здесь.Это то, что Зефир сделать довольно легко.Если бы не было post_load, это было бы именно то, что было бы получено как validated_input.
Второе задание схемы заключается в том, что ему нужно взять объект Python, извлеченный из DynamoDB, который выглядит почти точно так же, как пользовательский ввод JSON, за исключением числа с плавающей запятой, представляет собой десятичные дроби, и переводит его в мои объекты Python, Machine и Widget.Здесь мне нужно снова прочитать объект, но на этот раз использовать пост-загрузку для создания объектов.В этом случае, однако, я не хочу, чтобы мои числа были десятичными.Я бы хотел, чтобы они были стандартными поплавками Python.
Я мог бы написать для этого две совершенно разные схемы Marshmallow и, конечно же, покончить с этим.Можно иметь десятичные дроби для роста и веса, а можно просто плавать.Можно было бы загрузить сообщения для каждого объекта, а другого - нет.Но написание двух одинаковых схем - огромная боль.Мои определения схемы имеют длину в несколько сотен строк.Наследование версии БД с последующей загрузкой не выглядело как правильное направление, потому что мне нужно было бы изменить любые поля. Вложенные, чтобы указывать на правильный класс.Например, даже если бы я унаследовал MachineSchemaDBVersion от MachineSchema и добавил post_load, MachineScehemaDBVersion все равно будет ссылаться на WidgetScehema, а не на некоторую версию DB WidgetSchema, если только я не перенесу поле виджетов также.
Я мог бы потенциально получить свой собственныйОбъект схемы и флаг передают, находимся ли мы в режиме БД или нет.
Как люди обычно решают эту проблему, желая более или менее сохранять входные данные API REST непосредственно в DynamoDB с некоторой проверкой, а затем использовать этоданные позже для создания объектов Python для вычислений?
В методе, который я попробовал, моя схема всегда создает экземпляры моих объектов Python, а затем выводит их в базу данных, используя дампы из полностью сконструированного объекта.Проблема заключается в том, что объекты вычислительной библиотеки, в моем примере Machine или Widget, не имеют всех обязательных полей, которые мне нужно хранить в базе данных, таких как идентификаторы, имена или описания.Объекты созданы специально для вычислений.