【问题标题】:Adding external data to to SQLAlchemy object collection将外部数据添加到 SQLAlchemy 对象集合
【发布时间】:2018-06-09 22:54:55
【问题描述】:

我正在使用 flask-sqlalchemy 和 Postgres DB 构建基于 Flask 的 API 应用程序。

  • 数据库中的users 表为每个用户保存一条记录。这张桌子的PK是username(这里没什么特别的)
  • 数据库中的performance 表保存用户性能。此表的 PK 是一个日期。此表中的每个用户都有一个名为 username 的列。 (我知道有人说这种结构并不理想 - 但它是不相关的要求所必需的)。

例子:

PK = "username", tablename = "users"

| username | firstname | lastname | 
-----------------------------------
| alice    | Alice     | Johns    |
| bob      | Bob       | Speed    |


PK = "timestamp", tablename = "performance"

| timestamp | alice | bob  | 
-----------------------------------
| 2017-11-2 | 1     | 5    |
| 2017-11-3 | 6     | 9    |

我使用 SQLAlchemy 访问users 表。

我愿意创建 REST API,它将接收 Date 参数并返回所有用户的集合及其在该日期的表现。

什么是正确的 SQLAlchemy 查询,因此不会为每个用户单独选择性能值。

【问题讨论】:

  • 等等,你有一个表,每个用户都有一个列?或者你的意思是你有类似(date, user_id, value)的东西?
  • 一个时间序列表,每个用户有一列(用户数是静态的,不会变化)
  • 我从头开始重写了这个问题 - 你可以投票重新打开它吗? tnx

标签: python postgresql flask sqlalchemy marshmallow


【解决方案1】:

如果您绝对无法更改架构,那么您可以取消透视性能数据并加入用户。关系的示例映射,因为没有提供:

In [2]: class User(Base):
   ...:     __tablename__ = "users"
   ...:     username = Column(Unicode(255), primary_key=True)
   ...:     firstname = Column(Unicode(255))
   ...:     lastname = Column(Unicode(255))
   ...:     

In [3]: class Performance(Base):
   ...:     __tablename__ = "performance"
   ...:     timestamp = Column(Date, primary_key=True)
   ...:     # NOTE: this works **only** in a class body context,
   ...:     # and your schema shouldn't be like this anyway.
   ...:     for name in ['alice', 'bob']:
   ...:         locals()[name] = Column(Integer, nullable=False)
   ...:         

使用检查获取具有性能数据的用户名。您还可以保留用户名的静态列表:

In [11]: users = inspect(Performance).attrs.keys()[1:]

In [12]: users
Out[12]: ['alice', 'bob']

形成unpivot query——只有一种方法:

In [15]: from sqlalchemy.dialects import postgresql
    ...: performance = session.query(
    ...:         func.unnest(postgresql.array(users)).label('username'),
    ...:         func.unnest(postgresql.array(
    ...:             [getattr(Performance, name)
    ...:              for name in users])).label('value')).\
    ...:     filter(Performance.timestamp == '2017-11-2').\
    ...:     subquery()
    ...: 

将用户与他们的绩效价值联系起来:

In [24]: session.query(User, performance.c.value).\
    ...:     join(performance, performance.c.username == User.username).\
    ...:     all()
Out[24]: 
[(<__main__.User at 0x7f79eb5d2c88>, 1),
 (<__main__.User at 0x7f79eb5d2cf8>, 5)]

如果您要更改架构以将性能数据存储为 (timestamp, username, value) 元组,您可以简单地这样做:

In [2]: class User(Base):
   ...:     __tablename__ = "users"
   ...:     ...
   ...:     performance = relationship("BetterPerformance")
   ...:

In [25]: class BetterPerformance(Base):
    ...:     __tablename__ = "better_performance"
    ...:     timestamp = Column(Date, primary_key=True)
    ...:     username = Column(ForeignKey('users.username'), primary_key=True)
    ...:     value = Column(Integer, nullable=False)
    ...: 

In [13]: session.query(User, BetterPerformance.value).\
    ...:     join(User.performance).\
    ...:     filter(BetterPerformance.timestamp == '2017-11-2').\
    ...:     all()
Out[13]: 
[(<__main__.User at 0x7f6ae3282c18>, 1),
 (<__main__.User at 0x7f6ae3282ba8>, 5)]

甚至:

In [17]: session.query(User).\
    ...:     join(User.performance).\
    ...:     options(contains_eager(User.performance)).\
    ...:     filter(BetterPerformance.timestamp == '2017-11-2').\
    ...:     all()
Out[17]: [<__main__.User at 0x7f6ae3282c18>, <__main__.User at 0x7f6ae3282ba8>]

In [18]: [u.performance[0].value for u in _]
Out[18]: [1, 5]

【讨论】:

    猜你喜欢
    • 2014-12-09
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多