SQLAlchemy colonne calculée
(Nouveau SQLAlchemy alerte utilisateur) j'ai trois tables: une personne, les personnes taux horaire de départ à une date précise, et tous les jours des rapports de temps. Je suis à la recherche de la bonne façon d'avoir le coût d'une base de Temps hors de la personnes taux horaire de la journée.
Oui, je pourrais calculer la valeur au moment de la création et que, dans le cadre du modèle, mais pensez à cela comme un exemple de synthèse de données plus complexe derrière le rideau. Comment puis-je calculer le Temps.coût? Est-il un hybrid_propery, un column_property ou quelque chose de complètement différent?
class Person(Base):
__tablename__ = 'person'
personID = Column(Integer, primary_key=True)
name = Column(String(30), unique=True)
class Payrate(Base):
__tablename__ = 'payrate'
payrateID = Column(Integer, primary_key=True)
personID = Column(Integer, ForeignKey('person.personID'))
hourly = Column(Integer)
starting = Column(Date)
__tableargs__ =(UniqueConstraint('personID', 'starting',
name='uc_peron_starting'))
class Time(Base):
__tablename__ = 'entry'
entryID = Column(Integer, primary_key=True)
personID = Column(Integer, ForeignKey('person.personID'))
workedon = Column(Date)
hours = Column(Integer)
person = relationship("Person")
def __repr__(self):
return "<{date} {hours}hrs ${0.cost:.02f}>".format(self,
date=self.workedon.isoformat(), hours=to_hours(self.hours))
@property
def cost(self):
'''Cost of entry
'''
## This is where I am stuck in propery query creation
return self.hours * query(Payrate).filter(
and_(Payrate.personID==personID,
Payrate.starting<=workedon
).order_by(
Payrate.starting.desc())
Vous devez vous connecter pour publier un commentaire.
Le problème que vous avez ici, à résoudre élégamment que possible, utilise très avancé SQLAlchemy techniques, donc je sais que vous êtes un débutant, mais cette réponse va vous montrer tout le chemin à la fin. Cependant, la résolution d'un problème comme celui-ci exige de la marche à travers une étape à la fois, et vous pouvez obtenir la réponse que vous voulez de différentes manières que nous traversons.
Avant de vous lancer dans la façon de cet hybride ou autre, vous devez penser à la SQL. Comment pouvons-nous nous interroger pour le Moment.coût d'un arbitraire de la série de lignes? Nous pouvons lier le Temps à la Personne proprement parce que nous avons une simple clé étrangère. Mais pour lier le Temps de Payrate, avec ce schéma est un peu difficile, parce que le Temps des liens vers Payrate pas seulement via des person_id mais aussi via workedon - en SQL, nous aimerions participer à ce plus facilement à l'aide de "temps.person_id = la personne.l'id ET le temps.workedon ENTRE payrate.date_debut ET payrate.end_date". Mais vous n'avez pas de "end_date" ici, ce qui signifie que nous devons tirer aussi. Que la dérivation est la partie la plus délicate, de sorte que je suis venu avec commence comme ça (je l'ai mis en minuscule vos noms de colonne):
Il pourrait y avoir d'autres façons de faire cela, mais c'est ce que je suis venu avec - d'autres manières serait presque certainement certains même genre de chose (c'est à dire les sous-requêtes, jointures).
Donc avec un payrate de début/fin, nous pouvons comprendre ce qu'est une requête devrait ressembler. Nous voulons utiliser ENTRE correspondre à un moment de l'entrée à la plage de date, mais la dernière payrate entrée aura la valeur NULL pour la "fin" de la date, donc une façon de contourner ce problème est d'utiliser FUSENT à l'encontre d'une très grande date (l'autre est d'utiliser le conditionnel):
Maintenant ce que @hybride peut faire pour vous dans SQLAlchemy, lors de l'exécuter à l'expression SQL niveau, c'est exactement juste à l'entrée".heures * payrate_derived.horaire de la partie", c'est tout. Toutes les JOINDRE et il, vous aurez besoin de fournir à l'extérieur de l'hybride.
Nous avons donc besoin de bâton que de gros sous-requête dans ce:
Donc, essayons de comprendre ce que
<SOMETHING>
est. L'accumulation de SÉLECTIONNER un objet:La
cost()
hybride, sur l'expression de côté, il serait nécessaire de se référer à payrate_derived (nous ferons le python côté dans une minute):Ensuite, pour utiliser notre
cost()
hybride, il devrait être dans le contexte d'une requête qui a cette jointure. Remarque ici, nous utilisons Pythondatetime.date.max
pour obtenir que max date (pratique!):De sorte que la jointure est grand, et klunky, et nous avons besoin de le faire souvent, pour ne pas mentionner que nous allons avoir besoin de charger cette même collection en Python lorsque nous faisons de notre en-Python hybride. Nous pouvons la carte à l'aide de
relationship()
, ce qui signifie que nous avons à mettre en place des conditions de jointure, mais nous avons aussi besoin de la carte pour que la sous-requête, à l'aide d'un moins connus technique appelée non-primaire mappeur. Un non-primaire mapper vous permet de mapper une classe à l'arbitraire de la table ou de SÉLECTIONNER construire juste pour les fins de la sélection de lignes. Nous avons l'habitude de ne jamais avoir besoin d'utiliser cette Requête étant déjà nous permet de requête pour arbitraire, les colonnes et les sous-requêtes, mais pour sortir d'unrelationship()
il a besoin d'une cartographie. La cartographie a besoin d'une clé primaire à définir, et la relation a aussi besoin de savoir de quel côté de la relation est "étranger". C'est la partie la plus avancée ici et dans ce cas, il fonctionne comme ceci:De sorte que c'est la dernière que nous aurions du voir de cette jointure. Nous pouvons maintenant faire notre requête plus tôt que:
et, enfin, nous pouvons fil de notre nouveau
payrate
relation dans le Python niveau de l'hybride ainsi:La solution que nous avons ici pris beaucoup d'efforts, mais au moins la partie la plus complexe, qui payrate de cartographie, est entièrement en un seul endroit et nous n'avons jamais besoin de le regarder de nouveau.
Ici un exemple de travail:
De sortie (la première ligne est la version agrégée, le reste est de la par objet):
starting <= workedon order by starting DESC limit 1
De nombreuses fois, le meilleur conseil que je puisse donner est de simplement faire de différent. Un multi-table colonne calculée comme c'est ce que la base de données vues sont pour. Construire une vue basée sur la table de Temps (que vous voulez) avec votre colonne calculée en elle, de construire un modèle basé sur la vue, et vous êtes fixés. Ce sera certainement moins stressant sur la base de données. C'est aussi un bon exemple de pourquoi la limitation de la conception de ce qui peut être accompli par l'automatisation des les migrations est dangereux.