1

I'm working on a web application using Flask-SQLAlchemy.

My model is:

    class Product(db.Model):
    __tablename__ = 'product'
    id = db.Column(db.Integer, primary_key=True)
    ...

    product_to_category = db.relationship('ProductCategory',backref='product', lazy=True)
    shop_products = db.relationship('ShopProducts', backref='product', lazy=True)

    def __repr__(self):
        return '<Product: {}>'.format(self.id)


class ProductDescription(db.Model):
    __tablename__ = 'product_description'
    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))   
    name = db.Column(db.String(255), index=True, unique=False)
    ...

    def __repr__(self):
        return '<ProductDescription: {}>'.format(self.product_id)


class ProductImage(db.Model):
    __tablename__ = 'product_image'
    product_image_id = db.Column(db.Integer, primary_key=True, unique=True)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), index=True, unique=False)
    image = db.Column(db.String(255), nullable=True)
    ...

    def __repr__(self):
        return '<Product Image: {}>'.format(self.product_image_id)

class Category(db.Model):
    __tablename__ = 'category'
    id = db.Column(db.Integer, primary_key=True, unique=True)
    parent_id = db.Column(db.Integer, db.ForeignKey('category.id'), index=True, unique=False)
    ...

    products_to_category = db.relationship('ProductCategory',backref='category', lazy=True)

    def __repr__(self):
        return '<Category: {}>'.format(self.id)


class ProductCategory(db.Model):
    __tablename__ = 'product_category'
    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'))  
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))

    def __repr__(self):
        return '<Product Category: {}>'.format(self.product_id)


class Shops(db.Model):
    __tablename__ = 'shop'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), index=True, unique=False)
    description = db.Column(db.Text)
    ...

    shop_products = db.relationship('ShopProducts', backref='shop', lazy='joined')

    def __repr__(self):
        return '<Shop: {}>'.format(self.id)


class ShopProducts(db.Model):
    __tablename__ = 'shop_products'      
    id = db.Column(db.Integer, primary_key=True)
    shop_id = db.Column(db.Integer, db.ForeignKey('shop.id'))
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'),index=True)  
    price = db.Column(db.Float,index=True)
    ...

    def __repr__(self):
        return '<Shop Products: {}>'.format(self.price)

I want to make a query with filters to show all the products in a category and simultaneously show the lowest price from the 'shop_products' table.

My 'shop_products' table:

id shop_id product_id  price

1     1        1       34
2     1        2       56
3     1        3       67
4     2        1       35
5     2        2       55
6     2        3       68
7     3        1       32
8     3        2       58
9     3        4       69
10    4        1       101

I try with the following code but i see all products in my html page

@site.route('/category/<int:id>', methods=['GET', 'POST'])
def category(id):
    subq = ProductCategory.query.filter_by(category_id=id).subquery()
    product=db.session.query(ProductDescription,ProductImage.image,ShopProducts.price).\
    select_entity_from(subq).\
    filter(ProductDescription.product_id == ProductCategory.product_id). \
    filter(ProductImage.product_id ==  ProductCategory.product_id). \
    filter(ShopProducts.product_id ==  ProductCategory.product_id).\
    order_by(ShopProducts.price.asc())
    return render_template('site/category.html', product=product)

What can i do? Any ideas?

DamianM
  • 23
  • 4

2 Answers2

0

Try this.

sub = ProductCategory.query.filter_by(category_id=id).subquery()
products = db.session.query(
    ShopProducts).select_entity_from(sub).order_by(
        ShopProducts.price.asc()
        ).all()
simanacci
  • 2,197
  • 3
  • 26
  • 35
  • This solution doesn't work. I see only all product prices and not the lowest price for each product_id – DamianM Nov 07 '18 at 07:11
0

Your problem is that you forgot to join tables on respected column (product_id), so sqlalchemy made table of all possible combinations of entries from tables.

Try this one

# get all products in requested category
products_in_category = db.session.query(ProductCategory.product_id.label('c_p_id')).filter(
    ProductCategory.category_id==category_id
).subquery()

# get lowest price for each product
product_lowest_price = db.session.query(
    ShopProducts.product_id.label('p_id'), db.func.min(ShopProducts.price).label('lowest_price')
).group_by(ShopProducts.product_id).subquery()

# join all tables by product id
products = db.session.query(
    ProductDescription, ProductImage.image, product_lowest_price.c.lowest_price
).join(
    products_in_category, ProductDescription.product_id==products_in_category.c.c_p_id
).join(
    product_lowest_price, ProductDescription.product_id==product_lowest_price.c.p_id
).filter(ProductDescription.product_id==ProductImage.product_id)
  • You save my time. Works Perfectly. Just add an filter ".filter(ProductDescription.product_id == ProductImage.product_id)" and change in last line the "p_id" to "product_lowest_price.c.p_id" – DamianM Nov 07 '18 at 07:02
  • I recommend you to check this [question](https://stackoverflow.com/questions/5654278/sql-join-is-there-a-difference-between-using-on-or-where/5654338#5654338) on difference between using filter and join. – петр костюкевич Nov 07 '18 at 10:10