flask + sqlAlchemy COUNT, AVG and SUM in one query - python

Checked the sqlAlchemy docs but cannot see example of multiple columns query with filter and using FUNC.
How to compose a query based on my model to return result like this:
SELECT
COUNT(amount)a_cnt,
SUM(amount)a_sum,
AVG(amount)a_avg
FROM public.transaction
WHERE acc_id = 1
AND "traDate" >= '2019-11-20'
AND "traDate" <= '2019-12-01'
******************
a_cnt || a_sum || a_avg
------------------------
3 || 12 || 4
Please see below my model, and query functions, one with Class other with session, still unsure which one I should be using in this case. Both result in printing the query syntax.
Model:
class Transaction(db.Model):
id = db.Column(db.Integer, primary_key=True)
traDate = db.Column(db.Date, nullable=False)
amount = db.Column(db.Float, nullable=False)
desc = db.Column(db.String, nullable=False)
card = db.Column(db.String(1), nullable=False)
tag_id = db.Column(db.Integer, db.ForeignKey('tag.id'), nullable=True)
acc_id = db.Column(db.Integer, db.ForeignKey('account.id'), nullable=False)
uplDate = db.Column(db.DateTime, nullable=False, default=datetime.now)
### this?
def sum_filtered(account_id, date_from, date_to):
return db.session.query(db.func.count(Transaction.amount).label('a_cnt'), db.func.sum(Transaction.amount).label('a_sum'), db.func.avg(Transaction.amount).label('a_avg')).filter_by(acc_id = account_id).filter(Transaction.traDate >= date_from, Transaction.traDate <= date_to)
### OR this?
def sum_filtered(account_id, date_from, date_to):
return Transaction.query.with_entities(func.sum(Transaction.amount).label('a_sum')).filter_by(acc_id = account_id).filter(Transaction.traDate >= date_from, Transaction.traDate <= date_to)
app:
#app.route(...)
templateData = {
...
'total_amnt' : model.Transaction.sum_filtered(accountid, f_from, f_to),
...
}
return render_template('/list.html', **templateData)
html:
...
<span class="input-group-text">Total £{{ total_amnt }}</span><!-- shows the query syntax-->
<span class="input-group-text">Total £{{ total_amnt.a_sum }}</span><!-- shows nothing-->
...
What am I missing?

Found this Docs. If no better answer provided, I will accept this.
def sum_filtered(account_id, date_from, date_to):
result = db.session.execute('SELECT COUNT(amount)a_cnt, AVG(amount)a_avg, SUM(amount)a_sum FROM transaction WHERE acc_id = :p1 AND "traDate" BETWEEN :p2 AND :p3',{'p1' : account_id, 'p2' : date_from, 'p3' : date_to})
return result.fetchone()
App:
'sum_avg_cnt' : model.Transaction.sum_filtered(accountid, f_from, f_to),
Then html:
{{ sum_avg_cnt.a_cnt }}

Related

How to get data from multiple tables using flask-sqlalchemy

Here is my tables.
class maindevotee(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(225))
phonenumber = db.Column(db.String(225))
gothram = db.Column(db.String(225))
date = db.Column(db.String(50))
address = db.Column(db.String(250))
def json(self):
return {'id': self.id, 'name':self.name, 'phonenumber': self.phonenumber, 'gothram': self.gothram,
'date': self.date, 'address': self.address}
class relatives(db.Model):
id = db.Column(db.Integer, primary_key=True)
main_id = db.Column(db.Integer, db.ForeignKey('maindevotee.id'), nullable=False)
name = db.Column(db.String(225))
star = db.Column(db.String(225))
gender = db.Column(db.String(45))
relation = db.Column(db.String(45))
def json(self):
return {'main_id': self.main_id, 'name': self.name, 'star':self.star,
'gender': self.gender, 'relation': self.relation}
class services(db.Model):
id = db.Column(db.Integer, primary_key=True)
main_id = db.Column(db.Integer, db.ForeignKey('maindevotee.id'), nullable=False)
pooja = db.Column(db.String(225))
god = db.Column(db.String(225))
price = db.Column(db.Float)
donation = db.Column(db.String(225))
booking_fromdate = db.Column(db.String(50))
booking_todate = db.Column(db.String(50))
prasadam = db.Column(db.String(225))
def json(self):
return {'main_id': self.main_id, 'pooja': self.pooja, 'god': self.god,
'price': self.price, 'donation': self.donation, 'booking_fromdate': self.booking_fromdate,
'booking_todate': self.booking_todate, 'prasadam': self.prasadam}
How to get data from multiple tables in a single request. Here is my scource code to join the three tables.
If i am try to get data from database it will raise an error.
and the error is AttributeError: 'result' object has no attribute 'get_data'
can i get the data from database using foreign key.
data = db.session.query(maindevotee, relatives, services)\
.filter(maindevotee.phonenumber == 3251469870)\
.join(relatives, maindevotee.id == relatives.main_id)\
.join(services, maindevotee.id == services.main_id)\
.first()
def get_data():
return [data.json(get) for get in data.query.all()]
#app.route('/getdata/<phonenumber>',methods=['GET'])
def getdata():
return jsonify({'Devotee list': data.get_data()})
Correct
data = db.session.query(maindevotee, relatives, services)\
.filter(maindevotee.phonenumber == 3251469870)\
.join(relatives, maindevotee.id == relatives.main_id)\
.join(services, maindevotee.id == services.main_id)\
.first()
to
data = db.session.query(maindevotee, relatives, services)\
.filter(
(maindevotee.phonenumber == '3251469870')
& (maindevotee.id == relatives.main_id)
& (maindevotee.id == services.main_id)
).first()
for more clarifications, ask in the comments.
Upon comment
in
#app.route('/getdata/<phonenumber>',methods=['GET'])
def getdata():
return jsonify({'Devotee list': data.get_data()})
data contains the query results, that do not include the function get_data(), therefore you face the mentioned error.
Try the following modification, I think this is the result form you may want:
#app.route('/getdata/<phonenumber>',methods=['GET'])
def getdata():
return jsonify({**data.maindevotee.json(),**data.relatives.json(),**data.services.json()})
Good Luck

Order query by count of one-to-many relationship per hour - SQLAlchemy

I'm working on a video game auction website for buying/selling in-game items.
I want to be able to query the Auctions table and sort them by the "hottest" auctions. This is based on the number of bids/hour placed on an auction.
Here's the auction model:
class Auctions(db.Model):
id = db.Column(db.Integer, primary_key=True, index=True)
posted = db.Column(db.DateTime())
end = db.Column(db.DateTime())
...
bids = db.relationship('Bids', backref='auctions', lazy='dynamic', order_by='desc(Bids.amount)', cascade="all, delete-orphan")
Here's the Bids model:
class Bids(db.Model):
id = db.Column(db.Integer, primary_key=True, index=True)
bidder_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
auction_id = db.Column(db.Integer, db.ForeignKey('auctions.id'), index=True)
amount = db.Column(db.Integer)
posted = db.Column(db.DateTime())
I'm able to sort them by the amount of bids like this:
hot_stmt = db.session.query(models.Bids.auction_id, func.count('*').label('bid_count')).group_by(models.Bids.auction_id).subquery()
hot = db.session.query(models.Auctions, hot_stmt.c.bid_count).outerjoin(hot_stmt, (models.Auctions.id == hot_stmt.c.auction_id)).order_by(hot_stmt.c.bid_count.desc()).limit(5)
I can calculate and list bids/hour with this:
for auc, count in hot:
time_delta = datetime.utcnow() - auc.posted
auc_hours = time_delta.seconds / 60 / 60
print(auc.id, count / auc_hours)
How could I sort the query by bids/hour so that the query returns the top 5 hottest auctions?
One useful approach is to create a dictionary with auctions as keys and bids/hr as values:
d = {}
for auc, count in hot:
time_delta = datetime.utcnow() - auc.posted
auc_hours = time_delta.seconds / 60 / 60
d[auc] = count / auc_hours
Make a list of the auctions:
aucs = [auc for auc, count in hot]
Sort the list aucs based on the values (use the reverse keyword to put the highest values at the beginning of the list, since the sort function goes lowest-to-highest by default):
aucs.sort(key=d.get, reverse=True)

SQLalchemy/Flask - nested one to one

I'm trying to use Flask+SQLAlchemy to create a procedural universe. Each System has planets, and each planet has cities etc. etc.
It all seems pretty straight forward (famous last words) but I'm getting the following error:
' Traceback (most recent call last): line 75, in
print system.planets.cities
AttributeError: 'InstrumentedList' object has no attribute 'cities' '
when I run the below:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import libtcodpy as libtcod
import random
app = Flask(__name__)
db = SQLAlchemy(app)
class System(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
planets = db.relationship('Planet', backref='system')
def __repr__(self):
return '<System:{}>'.format(self.name)
class Planet(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
system_id = db.Column(db.Integer, db.ForeignKey('system.id'))
cities = db.relationship('City', backref='planet')
def __repr__(self):
return '<Planet:{}>'.format(self.name)
class City(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
pop = db.Column(db.Integer)
planet_id = db.Column(db.Integer, db.ForeignKey('planet.id'))
def __repr__(self):
return '<City:{}>'.format(self.name)
with app.app_context():
db.create_all()
def init_systems():
sysnum = random.randint(5,10)
#init namegenerator
libtcod.namegen_parse('data/systemnames.txt')
while sysnum > 0:
plannum = random.randint(3,8)
sname = libtcod.namegen_generate('Systems')
gensys = System(name=sname)
gensys.planets = []
while plannum > 0:
pname = libtcod.namegen_generate('Planets')
genplan = Planet(name=pname)
genplan.cities = []
citynum = random.randint(4,12)
while citynum > 0:
startpop = random.randint(10,1000)
cname = libtcod.namegen_generate('Cities')
gencit = City(name=cname, pop=startpop)
genplan.cities.append(gencit)
citynum -= 1
gensys.planets.append(genplan)
plannum -= 1
db.session.add(gensys)
db.session.commit()
sysnum -= 1
init_systems()
system = System.query.first()
print system.name
print system.planets
print system.planets.cities
Any ideas? I know there are other ways of structuring things, but I'm looking to have a pretty straightforward model of straight inheritance.
You can not access the cities by calling print system.planets.cities. As system.planets is a list of planets you need to access the cities for each planet separately.
for planet in system.planets:
print planet.cities

Filter on relationships

I'm trying to filter upon a relationship. In my db I've got awards and awards categories. I want retrieve only the featured and not nominated awards.
This is my model:
class AwardsAwardsCategory(Base):
__tablename__ = 'awards_awards_categories'
id = Column(Text, primary_key=True, default=generate_unique_id)
awards = relationship("Award")
award_id = Column(Text, ForeignKey("awards.id", ondelete='cascade'))
nomination = Column(Boolean)
def __json__(self, request):
return {
"id": self.id,
"nomination": self.nomination
}
class Award(Base):
__tablename__ = 'awards'
id = Column(Text, primary_key=True, default=generate_unique_id)
type = Column(Text)
featured = Column(Integer)
awards = relationship("AwardsAwardsCategory", lazy='joined')
def __json__(self, request):
return {
"id": self.id,
"type": self.type,
"number_of_awards": len(self.awards),
"featured": self.featured,
}
This is the call I make:
query = self.session.query(Award)
query = query.join(AwardsAwardsCategory.awards)
query = query.filter(Award.featured != 0)
query = query.filter(AwardsAwardsCategory.nomination != True)
q_results = query.all()
This results in the following query:
SELECT awards.id AS awards_id, awards.type AS awards_type, awards.featured AS awards_featured, awards_awards_categories_1.id AS awards_awards_categories_1_id, awards_awards_categories_1.award_id AS awards_awards_categories_1_award_id, awards_awards_categories_1.nomination AS awards_awards_categories_1_nomination
FROM awards_awards_categories JOIN awards ON awards.id = awards_awards_categories.award_id LEFT OUTER JOIN awards_awards_categories AS awards_awards_categories_1 ON awards.id = awards_awards_categories_1.award_id
WHERE awards.featured != 0 AND awards_awards_categories.nomination != true
It is almost correct except the WHERE clause is missing a condition:
AND awards_awards_categories_1.nomination != true
How can I change my code so that it adds the last condition to the WHERE clause.
I ended up filtering on the nomination on the application layer. It's nasty, but it works.
query = self.session.query(Award)
query = query.join(AwardsAwardsCategory.awards)
query = query.filter(Award.featured != 0)
query = query.filter(AwardsAwardsCategory.nomination != True)
q_results = query.all()
# remove all objects from the session in order to keep them in the db.
self.session.expunge_all()
for award_category in q_results:
# keep a separate list of the awards, in order to keep the iteration going as desired
awards = list(award_category)
for award in award_category.awards:
if award.nomination:
awards.remove(award)
def __json__(self, request):
return {
"id": self.id,
"type": self.type,
"number_of_awards": self.number_of_awards,
"featured": self.featured,
}
award_category.number_of_awards = len(awards)
award_category.__json__ = types.MethodType( __json__, a )
If anyone knows a how to do it better in SQLAlchemy please tell me!

Flask-SQLAlchemy join

Hello I have a problem with join at flask-sqlalchemy. I am a beginner at database and flask.
These are my classes:
class Shopping(db.Model):
__tablename__ = 'shoppings'
id = db.Column(db.Integer, primary_key=True)
product_name = db.Column(db.String(30), index=True, unique=False)
price=db.Column(db.Float(10), index=True)
date=db.Column(db.Date())
s_type_id = db.Column(db.Integer, db.ForeignKey('shopping_types.id'))
def __repr__(self):
return 'Alisveris yeri :{0} Tutar :{1} Tarih: {2}'.format(self.product_name,self.price,self.date)
def __list__(self):
return [self.product_name,self.price,self.date]
class Shopping_Type(db.Model):
__tablename__='shopping_types'
id=db.Column(db.Integer,primary_key=True)
type_name=db.Column(db.String(30), index=True, unique=True)
types = db.relationship('Shopping', backref = 'shopping_types', lazy = 'dynamic')
def __repr__(self):
return '{0}'.format(self.type_name)
when I try on python terminal and run:
select shoppings.product_name ,shoppings.price, shoppings.date, shopping_types.type_name from shoppings join shopping_types ON shoppings.s_type_id=shopping_types.id
query
I get what I want but when I run flask-sqlalchemy command:
rslt=db.session.query(spng).join(st)
spng:Shopping(class)
st:Shopping_Type(class)
I get only Shopping data.
I want to get Shopping + Shopping_Type data.
Thank you.
rslt = db.session.query(spng, st).join(st)
The result would be an enumerable of tuples of (Shopping, Shopping_Type)
rslt = db.session.query(spng, st).filter(spng.s_type_id == st.id).all()
for x, y in rslt:
print("Product ID: {} Product Name: {} Sopping Type: {}".format(x.id, x.product_name, y.tye_name))
type(rslt) is a tuple, contains elements as the number of tables joining.

Categories

Resources