gpt4 book ai didi

python - 代理模式习语

转载 作者:太空宇宙 更新时间:2023-11-04 01:40:18 24 4
gpt4 key购买 nike

我是一名 Web 应用程序开发人员,在使用 SQLAlchemy 时,我发现当我想要来自(比方说) 用户的特定 row 时,在我的许多 Controller 中执行此操作很笨拙 表:

from model import dbsession # SQLAlchemy SessionMaker instance
from model import User

user = dbsession().query(User).filter_by(some_kw_args).first()

或者说我想向表中添加一个用户(假设有另一个 Controller ):

from model import dbsession # SQLAlchemy SessionMaker instance
from model import User

user = User("someval", "anotherval", "yanv")
dbsession().add(user)

因此,由于这种笨拙(我不会讨论我的其他一些个人习语),我不喜欢为了向表中添加记录或从表中获取记录而不得不做所有这些.所以我决定(在对 SQLAlchemy 进行了大量令人讨厌的黑客攻击并决定我做了太多“神奇”的事情之后)这适合代理模式。

我(起初)在 model 模块中做了类似的事情:

def proxy_user(delete=False, *args, **kwargs):
session = DBSession()

# Keyword args? Let's instantiate it...
if (len(kwargs) > 0) and delete:
obj = session.query(User).filter_by(**kwargs).first()
session.delete(obj)

return True
elif len(kwargs) > 0:
kwargs.update({'removed' : False})
return session.query(User).filter_by(**kwargs).first()
else:
# Otherwise, let's create an empty one and add it to the session...
obj = User()
session.add(obj)
return obj

我对我的所有模型都这样做了(我知道这是令人讨厌的代码重复)并且效果很好。我可以将关键字参数传递给代理函数,它会为我处理所有 session 查询(甚至为已删除的标志提供默认的过滤器关键字)。我可以初始化一个空的模型对象,然后通过更新对象属性向它添加数据,所有这些更改都会被跟踪(并提交/刷新),因为该对象已被添加到 SQLAlchemy session 中。

因此,为了减少重复,我将大部分逻辑放在装饰器函数中,现在这样做:

def proxy_model(proxy):
"""Decorator for the proxy_model pattern."""

def wrapper(delete=False, *args, **kwargs):

model = proxy()

session = DBSession()

# Keyword args? Let's instantiate it...
if (len(kwargs) > 0) and delete:
obj = session.query(model).filter_by(**kwargs).first()
session.delete(obj)

return True
elif len(kwargs) > 0:
kwargs.update({'removed' : False})
return session.query(model).filter_by(**kwargs).first()
else:
# Otherwise, let's create an empty one and add it to the session...
obj = model()
session.add(obj)
return obj

return wrapper

# The proxy_model decorator is then used like so:
@proxy_model
def proxy_user(): return User

现在,在我的 Controller 中我可以这样做:

from model import proxy_user

# Fetch a user
user = proxy_user(email="someemail@ex.net") # Returns a user model filtered by that email

# Creating a new user, ZopeTransaction will handle the commit so I don't do it manually
new_user = proxy_user()
new_user.email = 'anotheremail@ex.net'
new_user.password = 'just an example'

如果我需要做其他更复杂的查询,如果我经常使用它,我通常会编写处理它的函数。如果这是一次性的事情,我将只导入 dbsession 实例,然后执行“标准”SQLAlchemy orm 查询....

这更干净,效果也很好,但我仍然觉得它没有完全“锁定”。任何其他人(或更有经验的 Python 程序员)是否可以提供更好的习惯用法来实现与我正在寻求的类似数量的清晰度,同时成为更清晰的抽象?

最佳答案

您提到“不喜欢必须做‘所有这些’”,其中“所有这些”看起来非常糟糕,就像只有 1 到 2 行代码,所以我觉得这并不是真正必要的。基本上我真的不认为你开始的任何一个陈述都那么冗长或令人困惑。

但是,如果我必须想出一种方法来表达这一点,我不会在这里使用装饰器,因为您实际上并没有装饰任何东西。如果没有应用装饰器,函数“proxy_user”实际上不会做任何事情。由于您需要以某种方式提供模型的名称,我认为您最好只使用一个函数并将模型类传递给它。我还认为将删除功能滚动到您的代理中是不合适的,并且根据您配置 Session 的方式,重复调用 DBSession() 可能会创建新的不相关 session ,如果您需要使用,这将导致问题同一事务中的多个对象。

无论如何,这是我如何将您的装饰器重构为一对函数的快速尝试:

def find_or_add(model, session, **kwargs):
if len(kwargs) > 0:
obj = session.query(model).filter_by(**kwargs).first()
if not obj:
obj = model(**kwargs)
session.add(obj)
else:
# Otherwise, let's create an empty one and add it to the session...
obj = model()
session.add(obj)
return obj

def find_and_delete(model, session, **kwargs):
deleted = False
obj = session.query(model).filter_by(**kwargs).first()
if obj:
session.delete(obj)
deleted = True
return deleted

同样,我不认为这是必要的,但我认为我可以同意:

user = find_or_add(User, mysession, email="bob@localhost.com")

可能比查找/创建用户并将其添加到 session 所需的直接 SQLAlchemy 代码更好看。

与您当前的装饰器方法相比,我更喜欢上述功能,因为:

  • 这些名称清楚地表明了您的意图,我觉得 proxy_user 并没有真正明确表示您想要一个用户对象(如果它存在),否则您想要创建它。
  • session 是明确管理的
  • 他们不需要我用装饰器包装每个模型
  • find_or_add 函数始终返回模型实例,而不是有时返回 True、查询结果集或模型实例。
  • find_and_delete 函数总是返回一个 bool 值,指示它是否能够成功找到并删除 kwargs 中指定的记录。

当然,您可能会考虑使用类装饰器将这些函数作为方法添加到您的模型类中,或者可能从包含此功能的基类派生您的模型,以便您可以执行以下操作:

# let's add a classmethod to User or its base class:
class User(...):
...
@classmethod
def find_or_add(cls, session, **kwargs):
if len(kwargs) > 0:
obj = session.query(cls).filter_by(**kwargs).first()
if not obj:
obj = cls(**kwargs)
session.add(obj)
else:
# Otherwise, let's create an empty one and add it to the session...
obj = cls()
session.add(obj)
return obj
...
user = User.find_or_add(session, email="someone@tld.com")

关于python - 代理模式习语,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5783544/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com