【问题标题】:Complex filter in joined tables SQLAlchemy连接表 SQLAlchemy 中的复杂过滤器
【发布时间】:2021-06-25 08:10:13
【问题描述】:

我有类似下面的查询

query = db.session.query(Attribute)
    .select_from(Product)
    .filter_by(category_id=some_id)
    .join(Attribute)

返回:

[(<Product 1>, <Attribute 1>),
 (<Product 1>, <Attribute 3>), #two attributes for Product 1
 (<Product 2>, <Attribute 2>),
 (<Product 2>, <Attribute 5>), #two attributes for Product 2
 ...]

我需要一个查询来保留至少一个属性匹配某些typevalue 值的所有产品。在上面的示例中,如果 &lt;Attribute 1&gt; 是唯一满足这些条件的属性,则结果应该是:

[(<Product 1>, <Attribute 1>), #Attribute1 met the conditions
 (<Product 1>, <Attribute 3>)] #Other Product1 rows are thus kept

编辑:产品和属性有多对多的关系,这里是模型:

class Product(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     sku = db.Column(db.String(10), unique=True, nullable=False)
     category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
     category = db.relationship('Category', backref=db.backref('products', lazy=True))

class ProductAttribute(db.Model):
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), primary_key=True)
    attribute_id = db.Column(db.Integer, db.ForeignKey('attribute.id'), primary_key

class Attribute(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.String(db.String(40))
    value = db.String(db.String(40))

【问题讨论】:

  • 能否提供AttributeProduct 的模型定义以帮助您查询?
  • 谢谢@van,我编辑了这个问题。请让我知道这是否有意义!

标签: python sqlalchemy orm


【解决方案1】:

解决此问题的一种方法是使用relationship.any 构造。

请在下面找到完整工作的示例代码,我试图在其中猜测更完整的模型。即使不完整,也会给你一个正确方向的提示:

型号:

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    sku = db.Column(db.String(10), unique=True, nullable=False)
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
    category = db.relationship('Category', backref=db.backref('products', lazy=True))
    # added this relationship definition
    attributes = db.relationship("Attribute", secondary="product_attribute")


class ProductAttribute(db.Model):
    product_id = db.Column(db.Integer, db.ForeignKey("product.id"), primary_key=True)
    attribute_id = db.Column(
        db.Integer, db.ForeignKey("attribute.id"), primary_key=True
    )


class Attribute(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String(40))
    value = db.Column(db.String(40))

测试数据:

attrs = ac_b, ac_w, as_l, as_s, ah_y, ah_n = [
    Attribute(type="color", value="black"),
    Attribute(type="color", value="green"),
    Attribute(type="size", value="large"),
    Attribute(type="size", value="small"),
    Attribute(type="healthy", value="yes"),
    Attribute(type="healthy", value="not that much"),
]

prods = [
    Product(sku="no-attribs", attributes=[]),
    Product(sku="black-only", attributes=[ac_b]),
    Product(sku="black-larg", attributes=[ac_b, as_l]),
    Product(sku="white-col", attributes=[ac_w, as_s]),
    Product(sku="no-colors", attributes=[ah_y, as_s]),
]

db.session.add_all(attrs + prods)

答案:查询

query = (
    db.session.query(Product, Attribute)
    # .select_from(Product)  # this could be removed if `Product` is included in the query above
    .join(Attribute, Product.attributes)
    # .filter_by(category_id=some_id)  # this one can be added, but not used in tests
    .filter(Product.attributes.any(
        # HERE you can put any combination of filters on the 'Attribute' you need
        and_(
            Attribute.type == 'color',
            Attribute.value == 'black',
        )
    ))
)

结果SQL: 在 postgres 上它应该如下所示:

SELECT product.id,
       product.sku,
       attribute.id,
       attribute.type,
       attribute.value
FROM product
JOIN product_attribute AS product_attribute_1 ON product.id = product_attribute_1.product_id
JOIN attribute ON attribute.id = product_attribute_1.attribute_id
WHERE EXISTS
    (SELECT 1
     FROM product_attribute,
          attribute
     WHERE product.id = product_attribute.product_id
       AND attribute.id = product_attribute.attribute_id
       AND attribute.type = %(type_1)s
       AND attribute.value = %(value_1)s)

查询结果:

如下所示,从测试数据中可以看出,结果涵盖了所有符合要求的产品的所有属性(“颜色”=“黑色”):

(<Product(id=3, sku='black-larg')>, <Attribute(id=1, type='color', value='black')>)
(<Product(id=3, sku='black-larg')>, <Attribute(id=3, type='size', value='large')>)
(<Product(id=2, sku='black-only')>, <Attribute(id=1, type='color', value='black')>)

【讨论】:

  • 嘿,抱歉花了这么长时间,效果很好,非常感谢!我觉得我不确定什么时候建立关系,或者什么时候完全依赖连接......在这方面有什么建议吗?效率上有什么区别?其他注意事项?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 2018-08-14
  • 1970-01-01
  • 2020-11-04
  • 2020-03-31
  • 2011-07-20
  • 1970-01-01
相关资源
最近更新 更多