Reputation: 369
I have a Product Model:
class Product(db.Model):
__tablename__ = 'product'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
offers = db.relationship('Offer', back_populates='product', lazy=True)
and a Offer Model:
class Offer(db.Model):
__tablename__ = 'offer'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
product = db.relationship('Product', back_populates='offers')
I would like to know how can I order a list of Products by the amount of offers each product has. I would like to be able to do something like:
sort = request.args.get('sort') # asc / desc
products = Product.query.order_by(len(Product.offers).sort).pagination([...])
I tried doing this but it only returns one Product:
query = db.session.query(Product).join(Product.offers)
if order == 'desc':
query = query.order_by(desc(func.count(Product.offers)))
else:
query = query.order_by(func.count(Product.offers))
query = query.filter(Product.has_offers == True)
products = query.paginate(page=page, per_page=50)
I will use this to be able to sort a HTML table by clicking on the table header. I am also sorting through other informations:
sort_by = dict(id=Product.id, mp_id=Product.marketplace_id,
updated=Product.last_seen, name=Product.name)
if sort_id in sort_by.keys():
sort = sort_by[sort_id]
sort = sort.desc() if order == 'desc' else sort.asc()
query = query.order_by(sort)
query = query.filter(Product.has_offers == True)
products = query.paginate(page=page, per_page=50)
Upvotes: 2
Views: 1456
Reputation: 52929
I tried doing this but it only gives me one
Product
:
You must be using MySQL 5.6 or older, since newer versions will not accept that query and raise an error:
ERROR 3029 (HY000): Expression #1 of ORDER BY contains aggregate function and applies to the result of a non-aggregated query
See the answers in "What can an aggregate function do in the ORDER BY clause?" for an explanation as to why you are getting one Product
as a result.
Your original approach is not that far from a working solution in the end. All you need to do is to add a suitable GROUP BY
clause that produces deterministic results in your query for it to work correctly:
order_op = desc if order == 'desc' else asc
query = db.session.query(Product).\
join(Product.offers).\
filter(Product.has_offers).\
group_by(Product.id).\
order_by(order_op(func.count()))
products = query.paginate(page=page, per_page=50)
The selected columns are determined by Product.id
, so you don't get indeterminate results.
Upvotes: 2