我有几个 ORM 映射表,(精简后)看起来像这样:
class Tag(Base):
__tablename__ = 'tags'
tag_name = Column(String, primary_key=True)
task2tag_assoc = Table('tasktags', Base.metadata,
Column('task_id', UUID, ForeignKey('tasks.task_id', ondelete='cascade'),
primary_key=True),
Column('tag_name', String, ForeignKey('tags.tag_name', ondelete='cascade'),
primary_key=True)
)
class Task(Base):
__tablename__ = 'tasks'
task_id = Column(UUID, primary_key=True)
_tags = relationship('Tag', secondary=task2tag_assoc, backref='tasks',
collection_class=set)
tags = association_proxy('_tags', 'tag_name')
def __init__(self, task_id, tags):
self.task_id = task_id
self.tags = set([tags])
通过此设置,我可以很好地创建带有新标签的任务。它创建标签
tags
表中的行,然后创建与新任务的关联
在 tasktags
表中很好。
t = Task(task_id = uuid4(), tags=['foo', 'bar']) #this works
当我尝试使用已存在的标签创建任务时,问题就来了
tags
表。
t2 = Task(task_oid = uuid4(), tags=['foo', 'baz']) #this will give an integrity error
似乎 SQLAlchemy 总是 尝试将标签插入到标签表中, 它是否已经存在。我真的很想只创建 如果标签已经存在则关联。这似乎很正常 在多对多的情况下,但我在文档中找不到任何地方 显示我可能做错了什么。
有没有办法获得我想要的行为?
作为背景,我使用带有 psycopg2 驱动程序的 postgresql 9.1 数据库,并且 SQLAlchemy 0.7.9(Python 2.7.3)
我正在考虑作为最后手段的事情:标签在技术上是一个主键,并且 没有别的,我可以只用一个 task_id->tag 表而没有标签 table 。但我希望能够将元数据附加到标签本身 必要时上路。
最佳答案
对于“仅限唯一标签”配方,我通常使用唯一对象配方,或者它的一些变体:http://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject .
这自然需要对特定行进行 SELECT 以判断它是否首先存在。 ORM 目前不直接支持“upsert”技术,它使用特定于数据库的命令根据数据库端的确定来插入或更新行。无论如何,您使用的是 Postgresql,它实际上不支持任何 native “upsert”功能,除了一个使用公用表表达式的非常笨拙的系统。
关于python - sqlalchemy 中的多对多。如果标记已存在,则防止 SQLAlchemy 插入表中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13149829/