This source详细说明了如何使用关联代理来创建具有ORM对象值的视图和对象。
但是,当我追加一个与数据库中现有对象匹配的值(并且所述值是唯一的或主键)时,它会创建一个冲突的对象,因此我无法提交。
所以在我看来这只是一个有用的视图,我需要使用ORM查询来检索要追加的对象。
这是我唯一的选择还是我可以使用merge(如果它是主键而不是唯一约束,我可能只能这样做),或者设置构造函数以便它将使用数据库中的现有对象如果它存在而不是创建一个新对象?
例如来自文档:
user.keywords.append('cheese inspector')
# Is translated by the association proxy into the operation:
user.kw.append(Keyword('cheese inspector'))
但是我想要翻译成更像的东西:(当然查询可能会失败)。
keyword = session.query(Keyword).filter(Keyword.keyword == 'cheese inspector').one()
user.kw.append(keyword)
或理想情况
user.kw.append(Keyword('cheese inspector'))
session.merge() # retrieves identical object from the database, or keeps new one
session.commit() # success!
我认为这可能不是一个好主意,但它可能在某些用例中:)
答案 0 :(得分:9)
您链接到的文档页面上显示的示例是composition
类型的关系(以OOP术语表示),因此代表owns
类型的关系,而不是uses
的关系动词因此,每个owner
都有自己的相同(就值而言)关键字的副本。
实际上,您可以完全使用您在问题中链接到的文档中的建议来创建自定义creator
方法,并将其破解为重用给定键的现有对象,而不是仅创建新对象。在这种情况下,User
类和creator
函数的示例代码如下所示:
def _keyword_find_or_create(kw):
keyword = Keyword.query.filter_by(keyword=kw).first()
if not(keyword):
keyword = Keyword(keyword=kw)
# if aufoflush=False used in the session, then uncomment below
#session.add(keyword)
#session.flush()
return keyword
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(64))
kw = relationship("Keyword", secondary=lambda: userkeywords_table)
keywords = association_proxy('kw', 'keyword',
creator=_keyword_find_or_create, # @note: this is the
)
答案 1 :(得分:3)
我最近遇到了同样的问题。 SQLAlchemy的创建者Mike Bayer向我推荐“unique object” recipe,但也向我展示了一个使用事件监听器的变体。后一种方法修改关联代理,以便UserKeyword.keyword
临时指向普通字符串,并且只有在关键字尚不存在时才创建新的关键字对象。
from sqlalchemy import event
# Same User and Keyword classes from documentation
class UserKeyword(Base):
__tablename__ = 'user_keywords'
# Columns
user_id = Column(Integer, ForeignKey(User.id), primary_key=True)
keyword_id = Column(Integer, ForeignKey(Keyword.id), primary_key=True)
special_key = Column(String(50))
# Bidirectional attribute/collection of 'user'/'user_keywords'
user = relationship(
User,
backref=backref(
'user_keywords',
cascade='all, delete-orphan'
)
)
# Reference to the 'Keyword' object
keyword = relationship(Keyword)
def __init__(self, keyword=None, user=None, special_key=None):
self._keyword_keyword = keyword_keyword # temporary, will turn into a
# Keyword when we attach to a
# Session
self.special_key = special_key
@property
def keyword_keyword(self):
if self.keyword is not None:
return self.keyword.keyword
else:
return self._keyword_keyword
@event.listens_for(Session, "after_attach")
def after_attach(session, instance):
# when UserKeyword objects are attached to a Session, figure out what
# Keyword in the database it should point to, or create a new one
if isinstance(instance, UserKeyword):
with session.no_autoflush:
keyword = session.query(Keyword).\
filter_by(keyword=instance._keyword_keyword).\
first()
if keyword is None:
keyword = Keyword(keyword=instance._keyword_keyword)
instance.keyword = keyword