Reputation: 131
class Category(Base):
__tablename__ = 'category'
__table_args__ = ({'schema': 'management'},)
category_id_seq = Sequence('management.category_id_seq')
id = Column(Integer, category_id_seq, server_default=category_id_seq.next_value(),
primary_key=True, unique=True, nullable=False, )
description = Column(String, nullable=False)
class Brand(Base):
__tablename__ = 'brand'
__table_args__ = ({'schema': 'management'},)
brand_id_seq = Sequence('management.brand_id_seq')
id = Column(Integer, brand_id_seq, server_default=brand_id_seq.next_value(),
primary_key=True, unique=True, nullable=False)
description = Column(String, nullable=False)
class Quality(Base):
__tablename__ = 'quality'
__table_args__ = ({'schema': 'management'},)
quality_id_seq = Sequence('management.quality_id_seq')
id = Column(Integer, quality_id_seq, server_default=quality_id_seq.next_value(),
primary_key=True, unique=True, nullable=False)
description = Column(String, nullable=False)
class Product(Base):
# region
__tablename__ = 'product'
__table_args__ = (UniqueConstraint('category_id', 'brand_id', 'description',
name='product_uc'), {'schema': 'operation'})
product_id_seq = Sequence('operation.product_id_seq')
id = Column(Integer, product_id_seq, server_default=product_id_seq.next_value(),
primary_key=True, unique=True, nullable=False)
category_id = Column(Integer, ForeignKey(
'management.category.id'), nullable=False)
brand_id = Column(Integer, ForeignKey(
'management.brand.id'), nullable=False)
description = Column(String, nullable=False)
category = relationship(
'Category', foreign_keys='[Product.category_id]')
brand = relationship(
'Brand', foreign_keys='[Product.brand_id]')
# endregion
@hybrid_property
def full_desc(self):
cte_product = session.query(func.concat(Category.description, ' - ',
Brand.description, ' - ',
self.description).label('desc')) \
.select_from(self) \
.join(Category, Brand) \
.filter(Category.id == self.category_id, Brand.id == self.brand_id) \
.cte('cte_product')
# return session.query(cte_product.c.desc).select_from(self).join(cte_product).filter(self.id == cte_product.c.id).cte('test')
return cte_product.c.desc
@full_desc.expression
def full_desc(cls):
cte_product = session.query(func.concat(Category.description, ' - ',
Brand.description, ' - ',
cls.description).label('desc')) \
.select_from(cls)\
.join(Category, Brand) \
.filter(Category.id == cls.category_id, Brand.id == cls.brand_id) \
.cte('cte_product')
return cte_product.c.desc
result_1 = session.query(Product.id, Product.full_desc).all()
for i in result_1:
print(i)
Running the code above results in a list of all 'full_desc' repeated for every single 'id', like so:
(1, 'Category6 - Brand1 - Product1')
(2, 'Category6 - Brand1 - Product1')
(3, 'Category6 - Brand1 - Product1')
(4, 'Category6 - Brand1 - Product1')
(5, 'Category6 - Brand1 - Product1')
(6, 'Category6 - Brand1 - Product1')
(7, 'Category6 - Brand1 - Product1')
(8, 'Category6 - Brand1 - Product1')
(9, 'Category6 - Brand1 - Product1')
(1, 'Category5 - Brand2 - Product2')
(2, 'Category5 - Brand2 - Product2')
(3, 'Category5 - Brand2 - Product2')
(4, 'Category5 - Brand2 - Product2')
... 81 rows in total
cte_product = session.query(Product.id.label('id'), func.concat(Category.description, ' - ',
Brand.description, ' - ',
Product.description).label('desc')) \
.select_from(Product) \
.join(Category, Brand) \
.filter(Category.id == Product.category_id, Brand.id == Product.brand_id) \
.cte('cte_product')
result_2 = session.query(cte_product.c.id, cte_product.c.desc).all()
for i in result_2:
print(i)
Running the code above results in a list of right desc for each id with no repetition (only 9 rows), like so:
(1, 'Category6 - Brand1 - Product1')
(2, 'Category5 - Brand2 - Product2')
(3, 'Category7 - Brand4 - Product3')
(4, 'Category7 - Brand3 - Product4')
(5, 'Category5 - Brand1 - Product5')
(6, 'Category7 - Brand5 - Product6')
(7, 'Category3 - Brand2 - Product7')
(8, 'Category1 - Brand3 - Product8')
(9, 'Category4 - Brand3 - Product9')
What should I change on the @hybrid_property and/or @hybridproperty.extension to get to the desired result as in Test 2 but using the methods in Test 1? Or is there a better way of doing this altogether?
SQLAlchemy==1.3.20
SQLAlchemy-Utils==0.36.7
PostgreSQL 13
Upvotes: 0
Views: 226
Reputation: 76992
I am not sure if you should use cte
at all. And the non-expression part of the @hybrid_property
should not use any queries.
Please see the code below, which should work (using sqlalchemy version 1.4):
@hybrid_property
def full_description(self):
return self.category.description + " - " + self.brand.description + " - " + self.description
@full_description.expression
def full_description(cls):
subq = (
select(
func.concat(
Category.description,
" - ",
Brand.description,
" - ",
cls.description,
)
)
.where(Category.id == cls.category_id).where(Brand.id == cls.brand_id)
.scalar_subquery()
.label("full_desc_subquery")
)
return subq
and respective queries:
# in-memory (not using expressions, but potentially loading categories and brands from the database)
result_1 = session.query(Product).all()
for product in result_1:
print(product.id, product.full_description)
# using .expression - get 'full_description' in one query.
result_2 = session.query(Product.id, Product.full_description).all()
for product_id, fulldesc in result_2:
print(product_id, fulldesc)
, where the latter would produce the following SQL
statement:
SELECT product.id,
(SELECT concat(category.description, %(concat_2)s, brand.description, %(concat_3)s, product.description) AS concat_1
FROM category,
brand
WHERE category.id = product.category_id
AND brand.id = product.brand_id) AS full_desc_subquery
FROM product
Upvotes: 1