Python 如何将nameko依赖项传递给SqlAlchemy事件处理程序?
我正在使用nameko和sqlalchemy编写一个小的RPC服务,它允许数据库的远程CRUD。对于某些方法/属性/事件处理程序,我的模型需要使用依赖项获取一些数据。按照我希望的工作方式,每当我在模型实例的生命周期内第一次调用其中一个方法时,数据都会被提取并缓存到模型上。之后,需要外部数据的方法将只使用缓存版本 我在设计这个时遇到了一些麻烦。将依赖项作为函数参数传递仅适用于模型方法。属性不允许传递参数,并且我无法控制如何调用SQL Alchemy的事件处理程序,因此我无法在那里注入任何依赖项。我发现实现这一点的唯一方法是在模型实例生命周期的早期将依赖项绑定到模型实例,但我觉得这与DI模式背道而驰 model.pyPython 如何将nameko依赖项传递给SqlAlchemy事件处理程序?,python,design-patterns,dependency-injection,sqlalchemy,nameko,Python,Design Patterns,Dependency Injection,Sqlalchemy,Nameko,我正在使用nameko和sqlalchemy编写一个小的RPC服务,它允许数据库的远程CRUD。对于某些方法/属性/事件处理程序,我的模型需要使用依赖项获取一些数据。按照我希望的工作方式,每当我在模型实例的生命周期内第一次调用其中一个方法时,数据都会被提取并缓存到模型上。之后,需要外部数据的方法将只使用缓存版本 我在设计这个时遇到了一些麻烦。将依赖项作为函数参数传递仅适用于模型方法。属性不允许传递参数,并且我无法控制如何调用SQL Alchemy的事件处理程序,因此我无法在那里注入任何依赖项。我
from uuid import uuid4
import sqlalchemy as sa
from sqlalchemy import event
from sqlalchemy.dialects import postgresql
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class Foo(Base):
__tablename__ = 'foo'
id = sa.Column(postgresql.UUID(as_uuid=True), primary_key=True, default=uuid4)
remote_id = sa.Column(sa.Integer, nullable=False, unique=True)
bar = sa.Column(sa.String, nullable=True)
def __init__(self, *args, **kwargs):
super(Foo, self).__init__(*args, **kwargs)
self._remote_data = None
@property
def remote_service(self):
# somehow return dependency
pass
@property
def remote_data(self):
if self._remote_data is None:
self._remote_data = self.remote_service.get(id=self.remote_id)
return self._remote_data
@property
def baz(self):
return self.bar + self.remote_data.baz
def do_before_insert(mapper, connection, foo):
# do something depending on value in foo.remote_data
pass
event.listen(Foo, 'before_insert', do_before_insert)
service.py
from nameko.extensions import DependencyProvider
from nameko.rpc import rpc
from nameko_sqlalchemy import Database
from .model import Base, Foo
class RemoteDataService(object):
def get(self, remote_id):
pass
class RemoteDataServiceProvider(DependencyProvider):
def get_dependency(self, worker_ctx):
return RemoteDataService()
class FooRPC:
name = "foo_rpc"
db = Database(Base)
@rpc
def get_foo(self, foo_id):
with self.db.get_session() as session:
foo = session.query(Foo).get(foo_id)
return foo
@rpc
def create_foo(self, remote_id, bar=None):
with self.db.get_session() as session:
foo = Foo(remote_id=remote_id, bar=bar)
session.add(foo)
session.commit()
return foo
我的一个解决方案涉及在会话创建时将依赖项提供程序返回的RemoteDataService
实例绑定到自定义数据库会话,然后使模型上的dependency属性如下所示:
from sqlalchemy.orm import object_session
...
@property
def remote_service(self):
return object_session(self).get_remote_service()
这解决了我的问题,但似乎不太符合DI。此外,它只适用于已经绑定到会话的模型,但在我的特定情况下,我可以接受它。尽管如此,如果有更好的解决方案,我会选择
我试图在DI/nameko/sqla领域中做的事情本身就是错误的吗?模型应该永远不直接处理依赖关系吗?无论哪种方式,人们如何协调SQLalchemy事件处理程序的使用(在典型情况下,您对函数调用几乎没有控制权)、DI以及在所述处理程序中使用依赖项的需要?有趣的问题。我不确定我的答案是你需要的。但也许这会有用。在任何情况下,您都可以看到不同的方法(总比没有好) 模型和依赖项。 不确定
模型
是调用某些服务或依赖项的好地方。我认为这是存储基于模型结构的微小逻辑的好地方。就这样。比如:
@property
def full_price(self):
return self.coefficient * self.price
@property
def full_address(self):
return ' '.join([self.country, self.city, self.street])
事件。插入之前的,更新之前的等
和模型一样。是更新/设置模型某些字段的好地方。比如:
foo.counter+=1
或foo.updated\u time=datetime.utcnow()
。但这不是处理某些“远程数据”的好地方
最好将所有其他逻辑(例如,保存/缓存/从任何服务检索数据,我的意思是self.remote\u service.get(…)
等)存储在另一层上
我试着用一个词来解释我的意思
注意!我没有与nameko合作,也不知道最佳实践等。所以这只是一个愿景
见回购协议中的注释。希望这有帮助。您是否尝试使用或?“我想这可能会有帮助。”丹妮拉·甘查谢谢你的建议。我将研究这些LIB,它们对于进行依赖注入似乎非常有用。但我不确定他们会如何使用nameko(对于这个项目,我希望继续使用nameko)。我没有使用nameko
,但我在生产中使用了inject
。好工具。我没有发现虫子或任何魔法。谢谢你的回答。我认为你是对的,我的方法是错误的。我有预感,这就是问题所在,但现在我更确信了。我将相应地重构我的代码,并在有意义的时候发回。我对你的答案投了赞成票,但它不会显示出来,因为我还没有足够的代表。这是正确的,因为在适当的位置将呼叫发送到远程服务。不过,在示例repo中,使用inject
库是多余的——您可以使用Nameko自己的DependencyProvider概念来实现同样的目的。@Matt我稍后详细阅读了Nameko
。是的,我完全同意你的看法。谢谢你的反馈。@Matt顺便说一句,谢谢你的活动Nameko
真是个好工具。谢谢@DanilaGanchar!