8

我有一些 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 DB 和 SQLAlchemy 0.7.9 (Python 2.7.3)

我正在考虑作为最后手段的事情:标签在技术上是一个主键,没有别的,我可以只使用一个 task_id->tag 表而没有标签表。但如果有必要,我希望能够将元数据附加到标签本身。

4

2 回答 2

4

对于“仅唯一标签”配方,我通常使用唯一对象配方或它的一些变体: http ://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject 。

这自然需要针对特定​​行进行 SELECT 以判断它是否首先存在。“upsert”技术,使用特定于数据库的命令来插入或更新基于数据库端确定的行,目前 ORM 不直接支持。无论如何,您使用的是 Postgresql,它实际上不支持任何本机“upsert”功能,除了一个使用公用表表达式的非常尴尬的系统。

于 2012-10-31T02:57:31.830 回答
0

尝试删除这样的关联链接 t2._tags = []

t2 = Task(task_oid = uuid4())
t2._tags = []
tags = ['foo', 'baz']
for tag in tags:
    t2._tags.append(Tag(tag))
于 2012-10-31T07:28:34.590 回答