如何正确使用SQLAlchemy的'@aggregated'类属性装饰器

时间:2017-07-11 03:26:28

标签: python flask sqlalchemy flask-sqlalchemy

我正在尝试使用SQLAlchemy的@aggregated装饰器为类(Receipt)定义属性('gross_amount)'。此gross_amount属性是外部ID与Item.gross_amount实例关联的所有Item实例的Receipt的总和。

I.E。,收据由物品组成,我想定义一个收据'gross_amount'值,它只是收据上所有物品的总金额。

我已在此文档http://sqlalchemy-utils.readthedocs.io/en/latest/aggregates.html

之后对我的代码进行了建模

所以它看起来像这样......

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.sql import func
from sqlalchemy import orm


class Receipt(Base):
    __tablename__ = "receipts"
    __table_args__ = {'extend_existing': True}
    id = Column(Integer, index = True, primary_key = True, nullable = False)


    @aggregated('itemz', Column(Integer))
    def gross_amount(self):
        return func.sum(Item.gross_amount)

    itemz = orm.relationship(
        'Item',
        backref='receipts'
    )


class Item(Base):
    __tablename__ = "items"
    id = Column(Integer, index = True, primary_key = True, nullable = False)

    '''
    FE relevant
    '''
    gross_amount = Column(Integer)
    receipt_id = Column(Integer, ForeignKey("receipts.id"), nullable=False)

在我的迁移中,我应该在receipts表中找到gross_amount的列吗? 1)当我在receipts表中定义此列时,任何实例的任何Receipt.gross_amount都指向receipts表中定义的gross_amount值。 2)当我不在receipts表中定义此列时,每当我对数据库执行SELECT时,我都会收到SQLAlchemy错误:

ProgrammingError: (psycopg2.ProgrammingError) column receipts.gross_amount does not exist

FWIW,我的SQLAlchemy包是最新通过PIP分发的...

SQLAlchemy==1.1.11
SQLAlchemy-Utils==0.32.14

我现在运行此操作的本地数据库是PostgreSQL 9.6.2

我在这里做错了什么?非常感谢任何患者的帮助!

2 个答案:

答案 0 :(得分:0)

是的,您需要将列添加到表中:

CREATE TABLE receipts (
    id INTEGER NOT NULL, 
    gross_amount INTEGER,  -- <<< See, it's here :) 
    PRIMARY KEY (id)
);
INSERT INTO receipts VALUES(1,7);
INSERT INTO receipts VALUES(2,7);
CREATE TABLE items (
    id INTEGER NOT NULL, 
    gross_amount INTEGER, 
    receipt_id INTEGER NOT NULL, 
    PRIMARY KEY (id), 
    FOREIGN KEY(receipt_id) REFERENCES receipts (id)
);

使用此自包含代码段进行测试:

from sqlalchemy import Column, Integer, ForeignKey, create_engine, orm
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy_utils import aggregated

Base = declarative_base()

class Receipt(Base):
    __tablename__ = "receipts"
    __table_args__ = {'extend_existing': True}
    id = Column(Integer, index = True, primary_key = True, nullable = False)

    @aggregated('itemz', Column(Integer))
    def gross_amount(self):
        return func.sum(Item.gross_amount)

    itemz = orm.relationship('Item', backref='receipts')

class Item(Base):
    __tablename__ = "items"
    id = Column(Integer, index = True, primary_key = True, nullable = False)   
    gross_amount = Column(Integer)
    receipt_id = Column(Integer, ForeignKey("receipts.id"), nullable=False)

    def __init__(self, amount):
        self.gross_amount=amount

engine = create_engine('sqlite:///xxx.db', echo=True)
Base.metadata.create_all(engine)

session = sessionmaker(bind=engine)()

receipt = Receipt()
receipt.itemz.append(Item(5))
receipt.itemz.append(Item(2))

session.add(receipt)
session.commit()

print (receipt.gross_amount)

当然,还有另一种称为hybrid_property的方法,它基本上允许您在不添加额外列的情况下执行数据库级和数据库级查询:

@hybrid_property
def gross_sum(self):
    return sum(i.gross_amount for i in self.itemz)

@gross_sum.expression
def gross_sum(cls):
    return select([func.sum(Item.gross_amount)]).\
            where(Item.receipt_id==cls.id).\
            label('gross_sum')

答案 1 :(得分:-1)

您收到此错误的原因是您尚未在数据库的gross_amount表中创建新添加的列(receipts)。

意思是,您当前的数据库表只有一个已创建的列(id)。要使聚合列起作用,它需要包含一个名为gross_amount的其他列。

此附加列必须允许null值。

这样做的一种方法是直接在PostgreSQL中使用SQL:

ALTER TABLE receipts ADD gross_amount int;

或者,如果还没有数据,您可以通过SQLAlchemy删除并重新创建表。它应该自动创建这个额外的列。

我不确定你最后一部分是什么意思:

  

当我在收据表中定义此列时,任何       任何实例的Receipt.gross_amount只指向gross_amount       收据表中定义的值。

它应该指向的地方。我不确定你的意思。您的意思是它不包含任何值,即使Item中的收据项有值吗?如果是这样,我会仔细检查是否是这种情况(并且根据their examples here,在查看结果之前刷新数据库会话。)