【发布时间】:2019-09-10 18:25:11
【问题描述】:
在我的Flask 应用程序中,我有类似于银行账户的东西:一个User 有一个Account,信用分录建模为Incomings,扣除建模为Outgoings。
问题:
获取一个用户的“帐户对帐单”,即每天的信用分录/扣除,例如
Thu 29 Aug 2019
Some deduction: -23.00
Some credit: 123.00
Fri 30 Aug 2019
Big credit: 4223.00
Another deduction: -42.00
我的数据模型:
这就是我的models.py 的(简化版)的样子:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Float, ForeignKey, Integer, Text, TIMESTAMP
from sqlalchemy.orm import relationship
Base = declarative_base()
class Account(Base):
__tablename__ = 'account'
id = Column(Integer, primary_key=True)
balance = Column(Float, nullable=False)
userID = Column(Integer, ForeignKey('user.id'))
incomings = relationship("Incoming", back_populates="account")
outgoings = relationship("Outgoing", back_populates="account")
user = relationship("User", back_populates="account")
class Incoming(Base):
__tablename__ = 'incoming'
id = Column(Integer, primary_key=True)
accountID = Column(Integer, ForeignKey('account.id'))
amount = Column(Float, nullable=False)
description = Column(Text, nullable=False)
timestamp = Column(TIMESTAMP, nullable=False)
account = relationship("Account", back_populates="incomings")
class Outgoing(Base):
__tablename__ = 'outgoing'
id = Column(Integer, primary_key=True)
accountID = Column(Integer, ForeignKey('account.id'))
amount = Column(Float, nullable=False)
description = Column(Text, nullable=False)
timestamp = Column(TIMESTAMP, nullable=False)
account = relationship("Account", back_populates="outgoings")
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(Text, nullable=False)
account = relationship("Account", back_populates="user")
我的一般预期方法:
- 获取用户的所有
Incomings,按天分组 - 获取用户的所有
Outgoings,按天分组 - 以某种方式合并两个列表,按天分组
我的背景:
自从我使用底层数据库PostgreSQL 以来已经有一段时间了(但后来,我确实设法设置了一个触发函数来自动更新余额),但到目前为止就SQLAlchemy(正在使用的ORM)而言,我似乎只是触及了表面。
第 1 步:获取用户的所有 Incomings,按天分组
按照第一个SO hit,我试过了
from sqlalchemy import func
# existing sample account ID
accountID = 42
# not relevant to the point at hand, known to work
db_session = get_a_scoped_session_from_elsewhere()
db_incomings = db_session.query(Incoming) \
.filter(Incoming.accountID == accountID) \
.group_by(func.day(Incoming.timestamp)) \
.all()
但这失败了
ProgrammingError: (psycopg2.errors.UndefinedFunction) ...
... function day(timestamp without time zone) does not exist
这似乎表明 PostgreSQL 不支持day。
根据this SO 的回答,
# imports and variables as above
db_incomings = db_session.query(Incoming) \
.filter(Incoming.accountID == accountID) \
.group_by(func.date_trunc('day', Incoming.timestamp)) \
.all()
适用于 PostgreSQL,但对我来说失败了
ProgrammingError: (psycopg2.errors.GroupingError) ...
... column "incoming.id" must appear in the GROUP BY clause ...
... or be used in an aggregate function
当我只是盲目地尝试按照错误消息告诉我的操作并将incoming.id 添加到GROUP BY 子句中
db_incomings = db_session.query(Incoming) \
.filter(Incoming.accountID == accountID) \
.group_by(Incoming.id,
func.date_trunc('day', Incoming.timestamp)) \
.all()
代码有效,但没有返回想要的结果;相反,我得到一个对象列表,例如
{'timestamp': datetime.datetime(2019, 8, 29, 10, 4, 27, 459000), 'id': 1, 'accountID': 42, ...}
{'timestamp': datetime.datetime(2019, 8, 29, 10, 8, 21, 493000), 'id': 2, 'accountID': 42, ...}
{'timestamp': datetime.datetime(2019, 8, 29, 10, 8, 42, 660000), 'id': 3, 'accountID': 42, ...}
这并不奇怪,考虑到我是按Incoming.id 分组的。
试图了解根本问题(参见例如here 或here),似乎我无法引用SELECT 语句中的字段(即SQLAlchemy .query)如果它没有出现在 GROUP BY 子句中(即 SQLAlchemy .group_by)。查看错误信息,反之亦然。
我已经绞尽脑汁好几个小时了,找到了很多 func.date_trunc 的替代品并打开了 800 个浏览器标签,但仍然不知道如何解决这个问题。
我的问题:我需要如何构建/构建 SQLAlchemy 查询?
【问题讨论】:
-
在您的查询中,您可以添加
with_entities并且不要在那里提及id列,那么您不需要将它包含在group_by中并且聚合应该可以正常工作(即您应该得到每天一排)
标签: python postgresql group-by sqlalchemy timestamp