Я разработчик веб-приложений, и при использовании SQLAlchemy я считаю неуклюжим делать это во многих моих контроллерах, когда мне нужна конкретная строка из (скажем) таблицы users
:
from model import dbsession # SQLAlchemy SessionMaker instance
from model import User
user = dbsession().query(User).filter_by(some_kw_args).first()
Или сказать, что я хочу добавить пользователя в таблицу (при условии, что другой контроллер):
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
Я сделал это для всех моих моделей (я знаю, что это неприятное дублирование кода), и это работает довольно хорошо. Я могу передать аргументы ключевого слова в функцию прокси, и она обрабатывает все запросы сеанса для меня (даже предоставляя ключевое слово фильтра по умолчанию для удаленного флага). Я могу инициализировать пустой объект модели, а затем добавить к нему данные, обновив атрибуты объекта, и все эти изменения отслеживаются (и фиксируются / сбрасываются), поскольку объект был добавлен в сеанс SQLAlchemy.
Итак, чтобы уменьшить дублирование, я поместил большую часть логики в функцию декоратора и теперь делаю это:
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
Итак, теперь в моих контроллерах я могу сделать это:
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) предложить лучшую идиому, которая позволила бы достичь той же степени ясности, которую я ищу, будучи более ясной абстракцией?