python – sqlalchemy中的多对多.如果标记已存在,则阻止SQLAlchemy插入表中

我有几个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])

通过这种设置,我可以创建一个带有新标签的任务.它创建了标签
标签表中的行,然后创建与新任务的关联
在tasktags表中很好.

t = Task(task_id = uuid4(), tags=['foo', 'bar']) #this works

当我尝试使用已存在的标记创建任务时,问题就出现了
标签表.

t2 = Task(task_oid = uuid4(), tags=['foo', 'baz']) #this will give an integrity error

似乎SQLAlchemy总是试图将标记插入到tags表中,
它是否已经存在.我真的很喜欢它只创造
标签已存在时的关联.这似乎是相当正常的
在多对多的情况下,但我在文档中找不到任何地方
显示我可能做错了什么.

有没有办法得到我想要的行为?

对于后台,我使用带有psycopg2驱动程序的postgresql 9.1 DB,以及
SQLAlchemy 0.7.9(Python 2.7.3)

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

最佳答案 对于“仅限于唯一标签”的配方,我通常使用独特的对象配方或其中的一些变体:
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject.

这自然需要针对特定​​行的SELECT,以便判断它是否首先存在.现在,ORM不直接支持“upsert”技术,它使用特定于数据库的命令来基于数据库端确定来插入或更新行.你总是在Postgresql上实际上不支持任何原生的“upsert”功能,除了一个使用公共表表达式的非常笨拙的系统.

点赞