Flask-restful, marshal_with + 嵌套数据
Flask-restful, marshal_with + nested data
我已经坚持了一段时间了。我的问题是我需要能够使用 marshal_with 并验证来自 POST 的嵌套字段。我的测试看起来像这样:
def test_user_can_apply_with_multiple_dogs(self):
data = {
# User data
'registration_type': "guest",
'first_name': 'Alex',
'last_name': 'Daro',
'phone_number': '805-910-9198',
'street': '13950 NW Passage',
'street2': '#208',
'city': 'Marina del Rey',
'state': 'CA',
'zipcode': '90292',
'photo': 'test_image.png',
'how_did_you_hear': 0,
#Dog data
'pets': [
{
'dog_photo': "dog.png",
'name': 'Genghis Khan',
'breed': 'Shih Tzu',
'age': 'Puppy',
'size': 'Small',
},
{
'dog_photo': "dog2.png",
'name': 'Archibald',
'breed': 'Great Dane',
'age': 'Adult',
'size': 'Extra Large',
},
]
}
resp = self.client.post('/profile/registration', data=json.dumps(data))
self.assertEqual(resp.status_code, 200)
我的端点 class 看起来像这样:
nested_fields = {
'dog_photo': fields.String,
'name': fields.String,
'breed': fields.String,
'age': fields.String,
'size': fields.String,
}
profile_fields = {
# 'user_email':fields.String,
'token': fields.String,
'registration_type': fields.String,
'first_name': fields.String,
'last_name': fields.String,
'phone_number': fields.String,
'street': fields.String,
'street2': fields.String,
'city': fields.String,
'state': fields.String,
'zipcode': fields.Integer,
'photo': fields.String,
'how_did_you_hear': fields.String,
#Dog data
'pets': fields.Nested(nested_fields)
}
class GuestProfile(Resource):
@marshal_with(profile_fields)
def post(self):
# User data
parser = reqparse.RequestParser()
parser.add_argument('registration_type', type=str)
parser.add_argument('first_name', type=str, required=True, help="First Name cannot be blank.")
parser.add_argument('last_name', type=str, required=True, help="Last Name cannot be blank.")
parser.add_argument('phone_number', type=str, required=True, help="Phone Number cannot be blank.")
parser.add_argument('street', type=str, required=True, help="Street cannot be blank.")
parser.add_argument('street2', type=str)
parser.add_argument('city', type=str, required=True, help="City cannot be blank.")
parser.add_argument('state', type=str, required=True, help="State cannot be blank.")
parser.add_argument('zipcode', type=str, required=True, help="Zipcode cannot be blank.")
parser.add_argument('photo', type=str, required=True, help="Please select a photo.")
parser.add_argument('how_did_you_hear', type=str, required=True, help="How did you hear about us cannot be "
"blank.")
parser.add_argument('pets', type=str)
kwargs = parser.parse_args()
print kwargs, "KWWW"
kwargs['pet'] 总是以 None
的形式出现。谁有想法?
您缺少 'Content-Type' header 以及 action='append'
宠物参数,如 here 所述。
我在上面测试了你的代码,只是添加了 'Content-Type' 和规定的动作,它在 Python 2.7 和 3 中都有效。
现在的问题是 returns 一个字符串列表,因为您将 pets 参数指定为 type=str
。为了获得字典列表,您必须编写自定义解析器类型(正如@Josh 正确指出的那样)。请参见下面的示例:
例如
def pet_parser(pet):
# You can do other things here too as suggested in @Josh's repsonse
return pet
在资源中:
parser.add_argument('pets', type=pet_parser, action='append')
在你的测试函数中:
headers = [('Content-Type', 'application/json')]
self.client.post('url', data=data, headers=headers)
Here's an example in the docs of how to make a custom parser type.
基本上,您定义一个函数:
- 获取从请求中提取的参数的原始值
- 如果解析或验证失败则引发
ValueError
- Returns验证成功的参数
与您的问题相关的基本示例:
def pet_list_parser(pets):
if type(pets) != list:
raise ValueError('Expected a list!')
# Do your validation of the pet objects here. For example:
for pet in pets:
if 'name' not in pet:
raise ValueError('Pet name is required')
# Also do any conversion of data types here
pet['name'] = pet['name'].capitalize()
return pets
parser = RequestParser()
parser.add_argument('pets', type=pet_list_parser)
您可能猜到了,这很快就会变得笨拙和烦人。 Flask-RESTful 中的请求解析不是为处理嵌套数据而设计的。它适用于查询字符串参数 headers、基本 JSON 等,并且可以配置为处理嵌套数据。但是,如果您要经常这样做,请为自己省去一些痛苦,并查看像 Marshmallow.
这样的编组库
我已经坚持了一段时间了。我的问题是我需要能够使用 marshal_with 并验证来自 POST 的嵌套字段。我的测试看起来像这样:
def test_user_can_apply_with_multiple_dogs(self):
data = {
# User data
'registration_type': "guest",
'first_name': 'Alex',
'last_name': 'Daro',
'phone_number': '805-910-9198',
'street': '13950 NW Passage',
'street2': '#208',
'city': 'Marina del Rey',
'state': 'CA',
'zipcode': '90292',
'photo': 'test_image.png',
'how_did_you_hear': 0,
#Dog data
'pets': [
{
'dog_photo': "dog.png",
'name': 'Genghis Khan',
'breed': 'Shih Tzu',
'age': 'Puppy',
'size': 'Small',
},
{
'dog_photo': "dog2.png",
'name': 'Archibald',
'breed': 'Great Dane',
'age': 'Adult',
'size': 'Extra Large',
},
]
}
resp = self.client.post('/profile/registration', data=json.dumps(data))
self.assertEqual(resp.status_code, 200)
我的端点 class 看起来像这样:
nested_fields = {
'dog_photo': fields.String,
'name': fields.String,
'breed': fields.String,
'age': fields.String,
'size': fields.String,
}
profile_fields = {
# 'user_email':fields.String,
'token': fields.String,
'registration_type': fields.String,
'first_name': fields.String,
'last_name': fields.String,
'phone_number': fields.String,
'street': fields.String,
'street2': fields.String,
'city': fields.String,
'state': fields.String,
'zipcode': fields.Integer,
'photo': fields.String,
'how_did_you_hear': fields.String,
#Dog data
'pets': fields.Nested(nested_fields)
}
class GuestProfile(Resource):
@marshal_with(profile_fields)
def post(self):
# User data
parser = reqparse.RequestParser()
parser.add_argument('registration_type', type=str)
parser.add_argument('first_name', type=str, required=True, help="First Name cannot be blank.")
parser.add_argument('last_name', type=str, required=True, help="Last Name cannot be blank.")
parser.add_argument('phone_number', type=str, required=True, help="Phone Number cannot be blank.")
parser.add_argument('street', type=str, required=True, help="Street cannot be blank.")
parser.add_argument('street2', type=str)
parser.add_argument('city', type=str, required=True, help="City cannot be blank.")
parser.add_argument('state', type=str, required=True, help="State cannot be blank.")
parser.add_argument('zipcode', type=str, required=True, help="Zipcode cannot be blank.")
parser.add_argument('photo', type=str, required=True, help="Please select a photo.")
parser.add_argument('how_did_you_hear', type=str, required=True, help="How did you hear about us cannot be "
"blank.")
parser.add_argument('pets', type=str)
kwargs = parser.parse_args()
print kwargs, "KWWW"
kwargs['pet'] 总是以 None
的形式出现。谁有想法?
您缺少 'Content-Type' header 以及 action='append'
宠物参数,如 here 所述。
我在上面测试了你的代码,只是添加了 'Content-Type' 和规定的动作,它在 Python 2.7 和 3 中都有效。
现在的问题是 returns 一个字符串列表,因为您将 pets 参数指定为 type=str
。为了获得字典列表,您必须编写自定义解析器类型(正如@Josh 正确指出的那样)。请参见下面的示例:
例如
def pet_parser(pet):
# You can do other things here too as suggested in @Josh's repsonse
return pet
在资源中:
parser.add_argument('pets', type=pet_parser, action='append')
在你的测试函数中:
headers = [('Content-Type', 'application/json')]
self.client.post('url', data=data, headers=headers)
Here's an example in the docs of how to make a custom parser type.
基本上,您定义一个函数:
- 获取从请求中提取的参数的原始值
- 如果解析或验证失败则引发
ValueError
- Returns验证成功的参数
与您的问题相关的基本示例:
def pet_list_parser(pets):
if type(pets) != list:
raise ValueError('Expected a list!')
# Do your validation of the pet objects here. For example:
for pet in pets:
if 'name' not in pet:
raise ValueError('Pet name is required')
# Also do any conversion of data types here
pet['name'] = pet['name'].capitalize()
return pets
parser = RequestParser()
parser.add_argument('pets', type=pet_list_parser)
您可能猜到了,这很快就会变得笨拙和烦人。 Flask-RESTful 中的请求解析不是为处理嵌套数据而设计的。它适用于查询字符串参数 headers、基本 JSON 等,并且可以配置为处理嵌套数据。但是,如果您要经常这样做,请为自己省去一些痛苦,并查看像 Marshmallow.
这样的编组库