在 SQLAlchemy 中创建类似集合的映射属性

Posted

技术标签:

【中文标题】在 SQLAlchemy 中创建类似集合的映射属性【英文标题】:Creating a set-like mapped attribute in SQLAlchemy 【发布时间】:2014-10-02 17:28:31 【问题描述】:

我已经做了一些阅读和谷歌搜索,但我仍然不确定是否有本机/内置方法可以在 SQLAlchemy 对象中创建映射属性,从而为类似方案创建类似集合的属性:

User                             UserTag
+----+-----------------+         +---------+-----------+
| id | email           |         | user_id | tag       |
+----+-----------------+         +---------+-----------+
| 1  | foo@example.com |         | 1       | footag    |
| 2  | bar@example.com |         | 1       | something |     
+----+-----------------+         | 1       | tagbar    |
                                 | 2       | something |
                                 | 2       | 3rdtag    |
                                 +---------+-----------+

这样每个 User 对象都有一个 User.tags 映射属性,它是(或行为类似)一组字符串 - 例如以下将起作用:

>>> user = session.query(User).filter(User.id == 1).one()
>>> print user.tags
set(['tagbar', 'something', 'footag'])

【问题讨论】:

你需要一套什么特别的东西?独特性? O(1) 查找? 好吧,两者都有,虽然老实说查找时间并不重要,因为我不希望映射值的数量很大;每个用户最多可以有 10 个左右的标签,主要是我想知道'tagbar' in user.tags。映射表在 (user_id, tag) 上定义了一个唯一键,因此在数据库级别保证了唯一性。对我来说,有一个集合或类似集合的数据结构才有意义。 【参考方案1】:

使用Association Proxy 来实现:

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    email = Column(String, nullable=False)
    tags = relationship('UserTag', collection_class=set,
                        cascade="all, delete, delete-orphan")
    tagset = association_proxy(
        'tags', 'tag_name',
        creator=lambda tag_name: UserTag(tag_name=tag_name),
    )

class UserTag(Base):
    __tablename__ = 'user_tags'
    user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
    tag_name = Column(String, primary_key=True)

然后您可以通过以下方式使用模型:

# create sample data
foo = User(
    email='foo@example.com',
    tagset='footag', 'something', 'tagbar'
)
bar = User(
    email='bar@example.com',
    tagset='something', '3rdtag'
)
session.add(foo)
session.add(bar)
session.commit()

bar = session.query(User).get(2)
assert 'something' in bar.tagset
bar.tagset.remove('something')
assert 'something' not in bar.tagset

同时添加具有相同tag_name 值的UserTag 将得到正确处理。

【讨论】:

以上是关于在 SQLAlchemy 中创建类似集合的映射属性的主要内容,如果未能解决你的问题,请参考以下文章

SqlAlchemy 将新字段添加到类并在表中创建相应的列

如何使用 Postgres 在 SQLAlchemy 中创建表?

JavaScript中创建对象的7种模式

Flask 学习-13.Flask-SQLAlchemy 新建模型和字段

使用 PostgreSQL 在 SQLAlchemy 测试中创建数据库

如何在 SQLAlchemy 1.2.7 中创建没有定义类的新表