我有一个可以通过PUT更新的Question对象。但是,如果问题已经回答,则某些字段将无法更新。执行PUT请求时,将发送所有字段(与PATCH请求不同);变化的和不变的。我的目标是在验证之前捕获未更改的字段,并将其从args中删除。
这是必须在许多视图中重复使用的内容,因此仅显式地进行操作的简单解决方案还不够好,因为这会在我的所有视图中添加很多样板代码。相反,我正在寻找一种几乎透明的解决方案,例如使用定制的use_put_kwargs
而非put_kwargs
从定制的Schema继承,为创建字段添加一个选项。没有什么太重了。
让我们从一个示例开始:
class QuestionGetArgs(Schema):
question_id = fields.Integer(required=True, location="view_args", validate=lambda x: x > 0)
@post_load
def deserialize(self, args):
args["question"] = QuestionDao.get_question_by_id(args.pop("question_id"))
class QuestionPutArgs(QuestionGetArgs):
title = fields.Str(location="json")
answer_type = fields.Str(location="json", validate=validate.OneOf("Open", "SingleChoice", "MultipleChoices"))
@post_load
def validate_question(self, args):
if "answer_type" in args and QuestionDao.has_answers(args["question"]):
raise ValidationError("Cannot edit question answer_type if there already are answers")
QuestionArgsValidator.validate_schema(self.get_updated_schema(args)) # validates that once updated, the question would still be valid
class Question(DefaultResource):
@use_kwargs(QuestionPutArgs)
def put(self, question, question_id, **kwargs):
try:
updated_question = QuestionDao.update_question(question, **kwargs)
except UniqueKeyViolation as e:
abort(409, str(e))
return self.jsonify(EntityDictBuilder.make_question(updated_question))
我没有填写所有问题的字段,只有我遇到的一个字段。
在这种状态下,由于我们不检查answer_type
是否已更改,因此回答问题的任何PUT都将失败。
我的问题如下:
args["question"]
必须加载,以便我们可以将args["answer_type"]
与args["question"].answer_type
进行比较。args["question"]
,就加载所有内容并调用post_load
,对于我来说,从args
中删除内容为时已晚。
args["question"]
,例如如果将解决方案应用于/clients/12/responses/32
而不是/questions/98
,则必须将字段与前者ID 32的响应字段和ID 98的问题进行比较。后者。到目前为止,我试图做的是制作自己的@use_put_kwargs
:
def use_put_kwargs(schema_cls, schema_kwargs=None, **kwargs):
schema_kwargs = schema_kwargs or {}
def factory(request):
# Filter based on 'fields' query parameter
only = request.args.get("fields", None)
# Respect partial updates for PATCH requests
partial = request.method == "PATCH"
schema = schema_cls(only=only, partial=partial, context={"request": request}, **schema_kwargs)
return UnloadNotModifiedSchemaDecorator(schema, list(request.json.keys()))
return use_kwargs(factory, **kwargs)
使用UnloadNotModifiedSchemaDecorator
作为修饰符(从设计模式的意义上讲,不是从python的意义上讲),将除load
之外的所有职责传递给其架构,{{1} }}的其他行为。
load
但是事实证明,在class UnloadNotModifiedSchemaDecorator(DefaultSchema):
def __init__(self, schema, body_params):
self.schema = schema
self.body_params = body_params
def load(self, data, many=None, partial=None):
loaded = self.schema.load(data, many, partial)
return loaded
def loads(self, json_data, many=None, *args, **kwargs):
loaded = self.schema.loads(json_data, many, *args, **kwargs)
return loaded
# copy all fields and methods from schema
@property
def declared_fields(self):
return self.schema.declared_fields
@property
def many(self):
return self.schema.many
[...]
def validate(self, data, many=None, partial=None):
return self.schema.validate(data, many, partial)
和loaded = self.schema.load(data, many, partial)
之间我什么也做不了,因为调用了return loaded
,现在捕获未修改的字段已经太晚了。
那么,如何在它们反序列化之后post_load
发生之前捕获它们?