2017-07-11 6 views
0

J'essaie d'utiliser @aggregated decorator de SQLAlchemy pour définir un attribut ('gross_amount)' pour une classe, Receipt. Cet attribut gross_amount est la somme des instances Item.gross_amount pour toutes les instances Item associées à l'instance Receipt par un ID étranger. I.E., un reçu est composé d'articles, et je veux définir une valeur de réception 'montant_brut' qui est juste le total de $ de tous les articles sur le reçu.Comment utiliser correctement le décorateur d'attribut de classe @aggregated de SQLAlchemy

J'ai calqué mon code après ce document http://sqlalchemy-utils.readthedocs.io/en/latest/aggregates.html

il semble donc comme ça ...

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) 

Dans ma migration, je suis censé avoir une colonne dans la table receipts pour gross_amount ? 1) Lorsque je DÉFINIE cette colonne dans la table receipts, toute Receipt.gross_amount pour n'importe quelle instance pointe juste vers les valeurs de gross_amount définies dans la table receipts. 2) Quand je ne définissez pas cette colonne dans la table receipts, je reçois une erreur SQLAlchemy chaque fois que j'exécute une SELECT contre la base de données:

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

FWIW, mon paquet SQLAlchemy est le dernier distribué à travers PIP. ..

SQLAlchemy==1.1.11 
SQLAlchemy-Utils==0.32.14 

Et mon db local sur lequel je suis en ce moment est pour PostgreSQL 9.6.2

Qu'est-ce que Je fais mal ici? Toute aide au patient serait grandement appréciée!

Répondre

-1

La raison pour laquelle vous obtenez cette erreur est que la nouvelle colonne que vous ajoutez (gross_amount) n'a pas été créée dans la table receipts de la base de données. Cela signifie que votre table de base de données actuelle n'a qu'une colonne créée (id). Pour que la colonne agrégée fonctionne, elle doit contenir une colonne supplémentaire appelée gross_amount.

Cette colonne supplémentaire doit autoriser les valeurs null.

Une façon de le faire de qui est via SQL directement dans PostgreSQL:

ALTER TABLE receipts ADD gross_amount int; 

Sinon, s'il n'y a pas encore de données, vous pouvez supprimer et recréer la table via SQLAlchemy. Il devrait créer cette colonne supplémentaire automatiquement.

Je ne suis pas sûr de ce que vous entendez par la dernière partie:

Quand je fais définir cette colonne dans le tableau des recettes, tout Receipt.gross_amount pour toute instance juste des points à l'gross_amount valeurs définies dans la table des recettes.

C'est là où il est censé pointer. Je ne suis pas sûr de ce que vous voulez dire par là.Voulez-vous dire qu'il ne contient aucune valeur, même s'il y a des valeurs pour les articles de ce reçu dans Item? Si c'est le cas, je revérifierais que c'est le cas (et par their examples here, rafraîchir la session de base de données avant de voir les résultats).

+0

Ceci ne fournit pas de réponse à la question. Une fois que vous avez suffisamment [réputation] (https://stackoverflow.com/help/whats-reputation) vous pourrez [commenter n'importe quel article] (https://stackoverflow.com/help/privileges/comment); Au lieu de cela, [fournissez des réponses qui ne nécessitent pas de précisions de la part du demandeur] (https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can- je-fais-à la place). - [De l'examen] (/ review/low-quality-posts/18633934) – thewaywewere

0

Oui, vous avez besoin d'ajouter la colonne à la table:

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) 
); 

Testé avec cette auto-contenu extrait:

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) 

Bien sûr, il y a aussi une autre approche appelée hybrid_property, qui vous permet fondamentalement de faire des requêtes au niveau de la base de données ou orm sans ajouter de colonne supplémentaire à votre base de données:

@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')