Stop using sa-filters for basic grid sorting

this just breaks if we need to use "aliased" models e.g. when sorting
and/or filtering by Product "regular price" column and similar.  so
now sorting more like we always used to, except for multi-column.

nb. this still assumes callers use `Grid.make_sorter()` when declaring
the sorters.  if caller must specify more custom/explicit sort logic
then it likely will not work and we'll have to add a workaround to
allow avoiding the common logic..but that's another day
This commit is contained in:
Lance Edgar 2023-10-21 16:10:36 -05:00
parent 421266e70c
commit 6d79766b24
2 changed files with 37 additions and 36 deletions

View file

@ -30,7 +30,6 @@ import logging
import sqlalchemy as sa
from sqlalchemy import orm
from sa_filters import apply_sort
from rattail.db.types import GPCType
from rattail.util import prettify, pretty_boolean, pretty_quantity
@ -1235,29 +1234,29 @@ class Grid(object):
# TODO: is there a better way to check for SA sorting?
if self.model_class:
# convert sort settings into a 'sortspec' for use with sa-filters
full_spec = []
# collect actual column sorters for order_by clause
sorters = []
for sorter in self.active_sorters:
sortkey = sorter['field']
sortdir = sorter['order']
sortfunc = self.sorters.get(sortkey)
if sortfunc:
spec = {
'sortkey': sortkey,
'model': sortfunc._class.__name__,
'field': sortfunc._column.key,
'direction': sortdir or 'asc',
}
full_spec.append(spec)
if not sortfunc:
log.warning("unknown sorter: %s", sorter)
continue
# apply joins needed for this sort spec
for spec in full_spec:
sortkey = spec['sortkey']
# join appropriate model if needed
if sortkey in self.joiners and sortkey not in self.joined:
data = self.joiners[sortkey](data)
self.joined.add(sortkey)
return apply_sort(data, full_spec)
# add column/dir to collection
sortdir = sorter['order']
sorters.append(getattr(sortfunc._column, sortdir)())
# apply sorting to query
if sorters:
data = data.order_by(*sorters)
return data
else:
# not a SQLAlchemy grid, custom sorter

View file

@ -160,12 +160,6 @@ class ProductView(MasterView):
'inventory_on_order',
]
# same, but for prices
RegularPrice = orm.aliased(model.ProductPrice)
CurrentPrice = orm.aliased(model.ProductPrice)
SalePrice = orm.aliased(model.ProductPrice)
TPRPrice = orm.aliased(model.ProductPrice)
def __init__(self, request):
super().__init__(request)
self.expose_label_printing = self.rattail_config.getbool(
@ -332,28 +326,34 @@ class ProductView(MasterView):
g.set_joiner('family', lambda q: q.outerjoin(model.Family))
g.set_filter('family', model.Family.name)
# regular_price
g.set_label('regular_price', "Reg. Price")
RegularPrice = orm.aliased(model.ProductPrice)
g.set_joiner('regular_price', lambda q: q.outerjoin(
self.RegularPrice, self.RegularPrice.uuid == model.Product.regular_price_uuid))
g.set_sorter('regular_price', self.RegularPrice.price)
g.set_filter('regular_price', self.RegularPrice.price, label="Regular Price")
RegularPrice, RegularPrice.uuid == model.Product.regular_price_uuid))
g.set_sorter('regular_price', RegularPrice.price)
g.set_filter('regular_price', RegularPrice.price, label="Regular Price")
# current_price
g.set_label('current_price', "Cur. Price")
g.set_renderer('current_price', self.render_current_price_for_grid)
CurrentPrice = orm.aliased(model.ProductPrice)
g.set_joiner('current_price', lambda q: q.outerjoin(
self.CurrentPrice, self.CurrentPrice.uuid == model.Product.current_price_uuid))
g.set_sorter('current_price', self.CurrentPrice.price)
g.set_filter('current_price', self.CurrentPrice.price, label="Current Price")
CurrentPrice, CurrentPrice.uuid == model.Product.current_price_uuid))
g.set_sorter('current_price', CurrentPrice.price)
g.set_filter('current_price', CurrentPrice.price, label="Current Price")
# tpr_price
TPRPrice = orm.aliased(model.ProductPrice)
g.set_joiner('tpr_price', lambda q: q.outerjoin(
self.TPRPrice, self.TPRPrice.uuid == model.Product.tpr_price_uuid))
g.set_filter('tpr_price', self.TPRPrice.price)
TPRPrice, TPRPrice.uuid == model.Product.tpr_price_uuid))
g.set_filter('tpr_price', TPRPrice.price)
# sale_price
SalePrice = orm.aliased(model.ProductPrice)
g.set_joiner('sale_price', lambda q: q.outerjoin(
self.SalePrice, self.SalePrice.uuid == model.Product.sale_price_uuid))
g.set_filter('sale_price', self.SalePrice.price)
SalePrice, SalePrice.uuid == model.Product.sale_price_uuid))
g.set_filter('sale_price', SalePrice.price)
# suggested_price
g.set_renderer('suggested_price', self.render_grid_suggested_price)
@ -402,7 +402,9 @@ class ProductView(MasterView):
return "${:0.2f}".format(cost.unit_cost)
def render_price(self, product, field):
if not product.not_for_sale:
# TODO: previously this rendered null (empty string) if
# product was marked "not for sale" - but why? important?
#if not product.not_for_sale:
price = product[field]
if price:
return self.products_handler.render_price(price)