繁体   English   中英

在SQLAlchemy中通过association_proxy分配一组新的多对多相关标签的最优雅方法是什么?

[英]What is the most elegant way to assign a new set of many-to-many related tags via association_proxy in SQLAlchemy?

这是我的Flask-SQLAlchemy声明性代码:

from sqlalchemy.ext.associationproxy import association_proxy
from my_flask_project import db


tagging = db.Table('tagging',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id', ondelete='cascade'),
              primary_key=True),
    db.Column('role_id', db.Integer, db.ForeignKey('role.id', ondelete='cascade'),
              primary_key=True)
)


class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), unique=True, nullable=False)

    def __init__(self, name=None):
        self.name = name


class Role(db.Model):

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id', ondelete='cascade'))
    user = db.relationship('User', backref=db.backref('roles', cascade='all',
                           lazy='dynamic'))
    ...
    tags = db.relationship('Tag', secondary=tagging, cascade='all',
                           backref=db.backref('roles', cascade='all'))
    tag_names = association_proxy('tags', 'name')

    __table_args__ = (
        db.UniqueConstraint('user_id', 'check_id'),
    )

我认为这是非常标准的多对多标记解决方案。 现在,我想获取一个角色的所有标签,并为该角色设置新的标签集。

第一个很简单:

print role.tags
print role.tag_names

但是,第二个让我整日迷失于我的Python代码:-(我以为我可以做到这一点:

role.tag_names[:] = ['red', 'blue', 'white']

...或至少使用role.tags[:] = ...类似,但是我发明的所有内容都引发了许多完整性错误,因为SQLAlchemy并未检查是否存在任何现有标签,而是试图将所有标签完全插入新实体。

我的最终解决方案是:

# cleanup input
tag_names = set(filter(None, tag_names))

# existings tags to be updated
to_update = [t for t in role.tags if t.name in tag_names]

# existing tags to be added
to_add = list(
    Tag.query.filter(Tag.name.in_(tag_names - set(role.tag_names)))
)

# tags to be created
existing_tags = to_update + to_add
to_create = [Tag(name) for name in tag_names - set([t.name for t in existing_tags])]

# assign new tags
role.tags[:] = existing_tags + to_create

# omitted bonus: find a way how to get rid of orphan tags

问题是:这真的是正确的解决方案吗? 有没有更优雅的方法来解决这个琐碎的问题? 我认为整个事情都与这个问题有关 也许我只是愚蠢,也许我使事情变得过于复杂...无论如何,谢谢您的任何建议!

实际上,SQLAlchemy确实通过调用Session.merge()检查对象是否存在。 但是它是通过身份(它的主键)来实现的。 最简单的解决方案是使name主键,然后一切正常。 当然,在这种情况下,三个表链将变得多余,除非您要向Tag添加一些其他字段(例如,计数器)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM