flask-restful マイクロフレームワークを使用して、ネストされたリソースを検証するRequestParser
の構築に問題があります。次の形式のJSONリソース形式が想定されているとします。
{
'a_list': [
{
'obj1': 1,
'obj2': 2,
'obj3': 3
},
{
'obj1': 1,
'obj2': 2,
'obj3': 3
}
]
}
a_list
の各項目はオブジェクトに対応しています:
class MyObject(object):
def __init__(self, obj1, obj2, obj3)
self.obj1 = obj1
self.obj2 = obj2
self.obj3 = obj3
...そして、次のようなフォームを使用してRequestParserを作成します。
from flask.ext.restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('a_list', type=MyObject, action='append')
...しかし、a_list
内の各辞書のネストされたMyObject
sをどのように検証しますか?あるいは、これは間違ったアプローチですか?
これに対応するAPIは、各MyObject
を基本的にオブジェクトリテラルとして扱い、それらの1つ以上がサービスに渡される場合があります。したがって、リソースの形式をフラット化しても、この状況では機能しません。
ネストされたオブジェクトのRequestParser
インスタンスを作成することで成功しました。通常どおり最初にルートオブジェクトを解析し、次にその結果を使用して、ネストされたオブジェクトのパーサーにフィードします。
トリックは、add_argument
メソッドのlocation
引数とparse_args
メソッドのreq
引数です。 RequestParser
が見ているものを操作できます。
次に例を示します。
root_parser = reqparse.RequestParser()
root_parser.add_argument('id', type=int)
root_parser.add_argument('name', type=str)
root_parser.add_argument('nested_one', type=dict)
root_parser.add_argument('nested_two', type=dict)
root_args = root_parser.parse_args()
nested_one_parser = reqparse.RequestParser()
nested_one_parser.add_argument('id', type=int, location=('nested_one',))
nested_one_args = nested_one_parser.parse_args(req=root_args)
nested_two_parser = reqparse.RequestParser()
nested_two_parser.add_argument('id', type=int, location=('nested_two',))
nested_two_args = nested_two_parser.parse_args(req=root_args)
cerberus などのデータ検証ツールを使用することをお勧めします。まず、オブジェクトの検証スキーマを定義し(ネストされたオブジェクトスキーマは this 段落で説明されています)、次にバリデーターを使用して、スキーマに対してリソースを検証します。検証が失敗した場合も、詳細なエラーメッセージが表示されます。
次の例では、場所のリストを検証します。
from cerberus import Validator
import json
def location_validator(value):
LOCATION_SCHEMA = {
'lat': {'required': True, 'type': 'float'},
'lng': {'required': True, 'type': 'float'}
}
v = Validator(LOCATION_SCHEMA)
if v.validate(value):
return value
else:
raise ValueError(json.dumps(v.errors))
引数は次のように定義されます。
parser.add_argument('location', type=location_validator, action='append')
ここでのtype
引数は、解析された値を返すか、無効な型でValueErrorを発生させる呼び出し可能なものにすぎないため、これのために独自の型バリデーターを作成することをお勧めします。バリデーターは次のようになります。
from flask.ext.restful import reqparse
def myobj(value):
try:
x = MyObj(**value)
except TypeError:
# Raise a ValueError, and maybe give it a good error string
raise ValueError("Invalid object")
except:
# Just in case you get more errors
raise ValueError
return x
#and now inside your views...
parser = reqparse.RequestParser()
parser.add_argument('a_list', type=myobj, action='append')
bbenne10sの回答 は本当に便利だと思いましたが、そのままではうまくいきませんでした。
私がこれをした方法はおそらく間違っていますが、それはうまくいきます。私の問題は、リストで受け取った値がwrapであるように、action='append'
が何をするのか理解できないことですが、何もしません私に感覚。誰かがコメントでこれの要点を説明できますか?
したがって、私がやったことは、自分のlisttype
を作成し、value
param内のリストを取得して、この方法でリストを反復処理することです。
from flask.ext.restful import reqparse
def myobjlist(value):
result = []
try:
for v in value:
x = MyObj(**v)
result.append(x)
except TypeError:
raise ValueError("Invalid object")
except:
raise ValueError
return result
#and now inside views...
parser = reqparse.RequestParser()
parser.add_argument('a_list', type=myobjlist)
本当にエレガントな解決策ではありませんが、少なくともそれは機能します。誰かが私たちを正しい方向に向けてくれることを願っています...
更新
bbenne10がコメントで述べたように 、action='append'
は、同じ名前のすべての引数をリストに追加するので、OPの場合は、有用。
reqparse
がネストされたオブジェクトのいずれも解析/検証していないという事実が気に入らなかったので、自分の解決策を繰り返しました。カスタムオブジェクト内でreqparse
を使用することです。 myobjlist
と入力します。
最初に、ネストされたオブジェクトを解析するときにリクエストとして渡すために、Request
の新しいサブクラスを宣言しました。
class NestedRequest(Request):
def __init__(self, json=None, req=request):
super(NestedRequest, self).__init__(req.environ, False, req.shallow)
self.nested_json = json
@property
def json(self):
return self.nested_json
このクラスはrequest.json
をオーバーライドして、解析対象のオブジェクトで新しいjsonを使用するようにします。次に、reqparse
パーサーをmyobjlist
に追加してすべての引数を解析し、exceptを追加して解析エラーをキャッチし、reqparse
メッセージを渡しました。
from flask.ext.restful import reqparse
from werkzeug.exceptions import ClientDisconnected
def myobjlist(value):
parser = reqparse.RequestParser()
parser.add_argument('obj1', type=int, required=True, help='No obj1 provided', location='json')
parser.add_argument('obj2', type=int, location='json')
parser.add_argument('obj3', type=int, location='json')
nested_request = NestedRequest()
result = []
try:
for v in value:
nested_request.nested_json = v
v = parser.parse_args(nested_request)
x = MyObj(**v)
result.append(x)
except TypeError:
raise ValueError("Invalid object")
except ClientDisconnected, e:
raise ValueError(e.data.get('message', "Parsing error") if e.data else "Parsing error")
except:
raise ValueError
return result
このように、ネストされたオブジェクトでさえreqparseを通じて解析され、そのエラーを表示します
最高評価のソリューションは 'strict = True'をサポートしていません。'strict= True 'がサポートされていない問題を解決するには、FakeRequestオブジェクトを作成してRequestParserをごまかします
class FakeRequest(dict):
def __setattr__(self, name, value):
object.__setattr__(self, name, value)
root_parser = reqparse.RequestParser()
root_parser.add_argument('id', type=int)
root_parser.add_argument('name', type=str)
root_parser.add_argument('nested_one', type=dict)
root_parser.add_argument('nested_two', type=dict)
root_args = root_parser.parse_args()
nested_one_parser = reqparse.RequestParser()
nested_one_parser.add_argument('id', type=int, location=('json',))
fake_request = FakeRequest()
setattr(fake_request, 'json', root_args['nested_one'])
setattr(fake_request, 'unparsed_arguments', {})
nested_one_args = nested_one_parser.parse_args(req=fake_request, strict=True)
fake_request = FakeRequest()
setattr(fake_request, 'json', root_args['nested_two'])
setattr(fake_request, 'unparsed_arguments', {})
nested_two_parser = reqparse.RequestParser()
nested_two_parser.add_argument('id', type=int, location=('json',))
nested_two_args = nested_two_parser.parse_args(req=fake_request, strict=True)
ところで:flask RESTfulはRequestParserをリッピングし、Marshmallowで置き換えます Linkage