【问题标题】:Flask-RESTful how to add_resource and pass it non-global dataFlask-RESTful 如何添加资源并将其传递给非全局数据
【发布时间】:2013-10-05 02:52:43
【问题描述】:

在 Flask-RESTful 示例应用程序 posted here 中,TODOS 集合是一个全局变量。

Todo资源注册后:

api.add_resource(Todo, '/todos/<string:todo_id>')

Todo 方法在处理 Web 请求时访问全局 TODOS 变量。

相反,我想在一个类中实例化 API 并传递一个 TODOS 集合,它是一个类变量而不是全局变量。

当使用 Flask-RESTful 时,允许 Resource 类中的方法在不使用全局变量的情况下访问调用类提供的变量的正确方法是什么?

【问题讨论】:

    标签: python rest flask flask-extensions


    【解决方案1】:

    add_resource 接受两个参数,resource_class_args 和 resource_class_kwargs,用于将参数传递给构造函数。 (source)

    所以你可以有一个资源:

    from flask_restful import Resource
    
    class TodoNext(Resource):
        def __init__(self, **kwargs):
            # smart_engine is a black box dependency
            self.smart_engine = kwargs['smart_engine']
    
        def get(self):
            return self.smart_engine.next_todo()
    

    您可以像这样将所需的依赖项注入 TodoNext:

    smart_engine = SmartEngine()
    
    api.add_resource(TodoNext, '/next',
        resource_class_kwargs={ 'smart_engine': smart_engine })
    

    【讨论】:

      【解决方案2】:

      看来我第一次没看懂你,你可以用classmethod来构造你的API。然后将其添加为资源

      from flask import Flask
      from flask.ext.restful import Api
      
      class SomeApi(Resource):
          def get(self):
              return self.response
      
          @classmethod
          def make_api(cls, response):
              cls.response = response
              return cls
      
      
      class KillerApp(object):
          def __init__(self):
              self.app = Flask()
              app_api = Api(self.app)
              MyApi = SomeAPI.make_api({"key": "value"})
              app_api.add_resource(MyApi, "/api/path")
      
          def run(self)
              self.app.run()
      
      
      KillerApp().run()
      

      【讨论】:

      • 我更正了问题中的变量名称以匹配链接到的示例。对于您的示例,我的问题是您将如何在 KillerApp 中定义 TODOS,然后允许 SomeApi 访问它,而不是将 TODOS 作为全局变量。
      • @Greg 在我看来 cls 是一个类对象而不是一个实例,对吧?如果是这样,那么 cls.response 在类上创建一个静态变量是有意义的。但在你的例子中,我看到 get 方法返回 self.response 这是实例本身的一个变量......你能解释一下吗?我对python有点陌生。谢谢!
      • @danfromisrael 您不一定必须使用类方法。但是与__init__ 相比,您可以选择将它们用作构造函数。这是一个非此即彼的情况,根据用户的要求,我决定使用类方法,因为它更适合他们的解决方案偏见
      【解决方案3】:

      基于@Greg 的回答,我在 init 方法中添加了初始化检查:

      为 flask-restful api 创建和调用 Todo 资源类:

      todo = Todo.create(InMemoryTodoRepository())
      api.add_resource(todo, '/api/todos/<todo_id>')
      

      Todo 资源类:

      from flask_restful import reqparse, abort, Resource
      from server.ApiResources.DTOs.TodoDTO import TodoDTO
      from server.Repositories.ITodoRepository import ITodoRepository
      from server.Utils.Exceptions import InvalidInstantiationError
      from server.Utils.GeneralUtils import member_exists
      
      
      class Todo(Resource):
          """shows a single todo item and lets you delete a todo item
              use the 'create' class method to instantiate the class
          """
          def __init__(self):
      
              if not member_exists(self, "todo_repository", of_type=ITodoRepository):
                  raise InvalidInstantiationError("Todo", "todo_repository", "ITodoRepository", "create")
      
              self._parser = reqparse.RequestParser()
              self._parser.add_argument('task', type=str)
      
          @classmethod
          def create(cls, todo_repository):
              """
              :param todo_repository: an instance of ITodoRepository
              :return: class object of Todo Resource
              """
              cls.todo_repository = todo_repository
              return cls
      

      member_exists 辅助方法:

       def member_exists(obj, member, of_type):
              member_value = getattr(obj, member, None)
      
              if member_value is None:
                  return False
      
              if not isinstance(member_value, of_type):
                  return False
      
              return True
      

      以及自定义异常类:

      class InvalidInstantiationError(Exception):
          def __init__(self, origin_class_name, missing_argument_name, missing_argument_type, instantiation_method_to_use):
      
              message = """Invalid instantiation for class '{class_name}':
                        missing instantiation argument '{arg}' of type '{arg_type}'.
                        Please use the '{method_name}' factory class method""" \
                  .format(class_name=origin_class_name,
                          arg=missing_argument_name,
                          arg_type=missing_argument_type,
                          method_name=instantiation_method_to_use)
      
              # Call the base class constructor with the parameters it needs
              super(InvalidInstantiationError, self).__init__(message)
      

      因此,尝试使用默认构造函数最终会得到这个异常:

      server.Utils.Exceptions.InvalidInstantiationError:类“Todo”的无效实例化: 缺少“ITodoRepository”类型的实例化参数“todo_repository”。 请使用“创建”工厂类方法

      编辑:这对于将依赖注入与 flask-restful api 资源类(带或不带 IoC)一起使用很有用

      编辑 2:

      我们甚至可以更简洁并添加另一个帮助功能(准备导入):

      def must_have(obj, member, of_type, use_method):
              if not member_exists(obj, member, of_type=of_type):
                  raise InvalidInstantiationError(obj.__class__.__name__,
                                                  member,
                                                  of_type.__name__,
                                                  use_method)
      

      然后像这样在构造函数中使用它:

      from server.Utils.GeneralUtils import must_have
      
      class Todo(Resource):
      
          def __init__(self):
      
              must_have(self, 
                        member="todo_repository", 
                        of_type=ITodoRepository, 
                        use_method=Todo.create.__name__)
      

      【讨论】:

        猜你喜欢
        • 2014-09-25
        • 1970-01-01
        • 2015-06-04
        • 2015-08-27
        • 2018-08-05
        • 1970-01-01
        • 2022-12-10
        • 1970-01-01
        • 2021-05-02
        相关资源
        最近更新 更多