编写带有可选参数的REST包装函数的Python方法

时间:2018-06-30 09:50:00

标签: python rest api wrapper

我有一个REST api,想用Python编写一个包装,供其他人使用。这是一个搜索API,每个参数都被视为AND

示例:

api/search/v1/search_parameters[words]=cat cute fluffy&search_parameters[author_id]=12345&search_parameters[filter][orientation]=horizontal

编写采用所有这些参数的函数的最Python方式是什么,必须指定至少一个search_parameters stringvalue

我的包装函数如下所示,但我迷失了用户可以为此搜索api调用输入多个搜索参数的方式:

def search(self):
    url = BASE_URL + search_param_url
    response = self.session.get(url)
    return response.json()

最后,用户应该只可以拨打类似api.search()

的电话

1 个答案:

答案 0 :(得分:1)

免责声明:这样的问题,例如什么是最Pythonic(最好/最漂亮)的方式,可能会引起不必要的讨论(并分散注意力),从而产生不确定的结果。我个人的建议,而不是重用社区特定部分的建议,首先是:在代码中以及在设计界面时保持一致。想想那些会使用它们的人(包括12个月内使用的自己)。以及“最佳”解决方案通常是预期目的的功能,不一定是通用常数(即使可能有或多或少推荐的方式)。就是这样。

如果我理解正确,则您的参数具有key=value对的性质(并且您会将其扩展为search_parameters[key]=value的URL)。虽然您的示例中的filterorientation事件使我失望……如果不正确,请描述更多,我可以重新考虑我的建议。为此,字典似乎提供了很好的选择。要获得一个,您的方法可以是:

def search(self, search_kwargs):
    ...

并且您希望您的用户传递一个参数(args_dict = {'string': 'xxx', ...}; c.search(args_dict))。或者:

def search(self, **kwargs):
    ...

并且您希望用户将键/值对作为方法(c.search(string='xxx'))的关键字参数传递。我可能会赞成前一种选择。在准备参数时,Dict是灵活的(是的,在后一种情况下,您也可以传递一个dict,但这超出了关键字参数扩展的目的;总是选择达到相同目标的简单选项)。

在任何情况下,您都可以接受dict(my_args代表以上两者之一)。检查您是否拥有至少一个必需的密钥:

not ('string' in my_args or 'value' in my_args):
    raise SearchParamsError("Require 'string' or 'value'.")

执行任何其他完整性检查。准备要附加到URL的参数:

url_params = '&'.join(('{}={}'.format(k, my_dict[k]) for k in my_dict))

那是琐碎的东西。但是,根据您的需求和用法,您实际上可以引入一个SearchRequest类,该类的构造函数可以采用类似于上述方法的初始参数集,但是您将拥有允许操作该方法的其他方法。在执行之前搜索(添加更多参数)。并且每个参数添加可能已经经过有效性检查。您可以使该实例可调用以执行搜索本身(相应的方法),或将其传递给以准备好的请求作为其参数的搜索方法。


根据评论中的更多见识进行了更新。

如果您的API实际上使用(任意)嵌套的映射对象,则字典仍然是保存参数的良好结构。我会选择两个选项之一。

您可以使用嵌套词典,这可以为您提供描述请求的灵活性,并且可以更准确地反映REST API如何理解其数据->形成请求的方式与REST API的描述更为相似。但是,不再使用上面提到的关键字参数(或者没有与下一个选项相似的额外工作和更多翻译)。数据的结构可能会使使用起来不太方便(尤其是简单的情况)。例如:

my_dict = {'string': 'foo bar',
           'author_id': 12345,
           'filter': {'orientation': 'horizontal',
                      'other': 'baz'},
           'other': {'more': {'nested': 1,
                              'also': 2},
                     'less': 'flat'}}

def par_dict_format(in_dict, *, _pfx='search_parameters'):
    ret = []
    for key, value in in_dict.items():
        if isinstance(value, dict):
            ret.append(par_dict_format(value, _pfx='{}[{}]'.format(_pfx, key)))
        else:
            ret.append('{}[{}]={}'.format(_pfx, key, value))
    return '&'.join(ret)

或者,您也可以选择平面键/值对的结构,这些结构使用合理且无冲突的分隔符为各个元素引入表示法。根据使用的分隔符,您甚至可以使关键字参数重新发挥作用(尽管在我的示例中不是.)。缺点之一是,您无法有效地创建新的/并行接口和符号。例如:

my_dict = {'string': 'foo bar',
           'author_id': 12345,
           'filter.orientation': 'horizontal',
           'filter.other': 'baz',
           'other.more.nested': 1,
           'other.more.also': 2,
           'other.more.also': 2,
           'other.less': 'flat'}

def par_dict_format(in_dict):
    ret = []
    for key, value in in_dict.items():
        key_str = ''.join(('[{}]'.format(p) for p in key.split('.')))
        ret.append('{}={}'.format(key_str, value))
    return '&'.join(('search_parameters{}'.format(i) for i in ret))

我对这两个的看法是。如果我主要以编程方式构造查询(例如,使用不同的方法来启动不同的查询),那么我倾向于嵌套字典。如果预期的用法更适合于直接编写查询,调用search方法甚至可能通过CLI公开查询的人们,则后者(平面)结构可能更易于使用/编写。