使用Flask-SQLAlchemy和Flask-Marshmallow进行Aggreate连接查询

时间:2017-01-23 16:13:26

标签: python flask sqlalchemy flask-sqlalchemy marshmallow

我正在使用Flask-SQLAlchemy和Flask-Marshmallow来针对SQL Server实例提取数据。我尝试复制的查询看起来像这样

SELECT
    o.[ProductId],
    [AccountName] = a.[Name],
    [AccountDescription] = a.[Description],
    [TotalSize] = SUM(d.[Size])
FROM
    [OrderDetail] d
JOIN
    [Order] o
ON
    d.[OrderId] = o.[Id]
JOIN
    [Account] a
ON
    d.[AccountId] = a.[Id]
WHERE
    o.[Timestamp] <= @asOfDate
GROUP BY
    o.[ProductId],
    a.[Name],
    a.[Description]

我很难将其转换为SQLAlchemy,特别是Flask-SQLAlchemy,它似乎有一种略微不同的做法。

这是我的尝试,位于我的路线功能中。此查询执行,但针对服务器运行的SQL只是三个表的连接 - 它不进行聚合或分组。我认为它也会选择每一列,而不仅仅是我在with_entities中指定的列。

"""views.py"""
@app.route('/product_sizes')
def get_product_sizes() -> Response:
    as_of_date_str = request.args.get('asOfDate')
    query = OrderDetail.query
                       .with_entities(Order.product_id.label('product_id'),
                                      Account.name.label('account_name'),
                                      Account.description.label('account_description'),
                                      func.sum(OrderDetail.size).label('total_size')) \
                       .group_by('product_id', 'account_name', 'account_description')

    if as_of_date_str:
        as_of_date = datetime.strptime(as_of_date_str, '%Y-%m-%d')
        query = query.join(OrderDetail.order).filter(Order.timestamp <= as_of_date)

    result = schemas.product_size_schema.dump(query).data
    json_string = json.dumps(result, cls=DefaultJsonEncoder)
    return Response(response=json_string, status=200, mimetype="application/json")

我的问题是

  1. 如何修复查询,使其行为类似于上面的SQL查询?
  2. 这大致是&#34;正确&#34;做事的方式?我是SQLAlchemy和Flask的新手,所以我不确定正常的做事方式。例如,为每个列提供标签然后按这些字符串分组似乎有点偏离 - 我本来希望能够按照我的模型类的列属性进行分组。在尝试对联接表进行过滤时调用join似乎是多余的 - 不是已经在db.relationship属性中编码的连接?
  3. 我的模型和架​​构如下。

    """models.py"""
    class Account(db.Model):
        _id = db.Column('Id', db.Integer, primary_key=True)
        name = db.Column('Name', db.Unicode(250))
        description = db.Column('Description', db.Unicode)
    
    class Order(db.Model):
        _id = db.Column('Id', db.Integer, primary_key=True)
        product_id = db.Column('ProductId', db.Integer)
        timestamp = db.Column('TradeTimestamp', db.DateTime)
    
    class OrderDetail(db.Model):
        _id = db.Column('Id', db.Integer, primary_key=True)
        order_id = db.Column('OrderId', db.ForeignKey('Order.Id'), index=True)
        account_id = db.Column('AccountId', db.ForeignKey('Account.Id'), index=True)
        size = db.Column('Size', db.Numeric(19, 6), nullable=False)
    
        account = db.relationship('Account', primaryjoin='OrderDetail.account_id == Account._id', backref='order_details', lazy='joined')
        order = db.relationship('Trade', primaryjoin='OrderDetail.order_id == Order._id', backref='order_details', lazy='joined')
    
    
    """schemas.py"""
    class ProductSizeSchema(ma.ModelSchema):
        """Schema for serializing product size objects"""
    
        product_id = fields.Int()
        account_name = fields.Str()
        account_description = fields.Str()
        total_size = fields.Decimal(19, 6)
    
    product_size_schema = ProductSizeSchema()
    product_sizes_schema = ProductSizeSchema(many=True)
    

1 个答案:

答案 0 :(得分:3)

环顾四周之后,我读到如果不方便的话,不使用Flask-SQLAlchemy查询方法(例如MyModel.query.<something>)并不是什么大不了的事。所以我只是以正常的SQLAlchemy方式使用db.session.query()

query = db.session \
          .query(Order.instrument_id,
                 Account.name.label("account_name"),
                 Account.description.label("account_description"),
                 func.sum(OrderDetail.size).label("total_size")) \
          .join(OrderDetail.order) \
          .join(OrderDetail.account) \
          .group_by(Order.instrument_id, Account.name, Account.description)

if as_of_date_str:
    as_of_date = datetime.strptime(as_of_date_str, '%Y-%m-%d')
    query = query.filter(Order.timestamp <= as_of_date)