Untitled
unknown
plain_text
9 months ago
7.5 kB
16
Indexable
def catalog_browse(request):
"""
HTMX endpoint for filtering products by categories and chains.
Returns filtered products based on selected category IDs and chain IDs.
"""
#########################################################
# Parse query parameters
#########################################################
# Parse category IDs from query params - Django standard way
# Handles both formats: ?category=1&category=2&category=3 and ?category=1,2,3
category_params = request.GET.getlist('category')
if len(category_params) == 1 and ',' in category_params[0]:
# Handle comma-separated format (for backwards compatibility)
category_params = category_params[0].split(',')
selected_category_ids = [int(id) for id in category_params if id.isdigit()]
# Parse chain IDs from query params
chain_params = request.GET.getlist('chain')
selected_chain_ids = [int(id) for id in chain_params if id.isdigit()]
# Get categories and their descendants
selected_categories = ProductCategory.objects.filter(id__in=selected_category_ids)
all_category_ids = []
for category in selected_categories:
descendants = category.get_descendants(include_self=True)
all_category_ids.extend(descendants.values_list('id', flat=True))
# Remove duplicates
all_category_ids = list(set(all_category_ids))
# Apply search filter if provided
search_query = request.GET.get('query')
# Location-based filtering
has_user_location = LocationService.has_user_location(request)
# Get price history cutoff date
price_history_cutoff_date = timezone.now().date() - timezone.timedelta(days=settings.SEARCH_DAYS_AGO)
# Get nearby stores for location-based filtering
stores = LocationService.get_stores_within_user_radius(request)
stores_ids = [store.id for store in stores]
# If chain filters are set, filter stores by selected chains
if selected_chain_ids:
stores_ids = [store.id for store in stores if store.chain_id in selected_chain_ids]
#########################################################
# Build and execute products query
#########################################################
# Build base product query based on selected categories
products_query = Product.objects.filter(
categories__id__in=all_category_ids
).distinct()
# apply search if search query is provided
if search_query:
products_query = ProductSearchService.search_catalog(
search_query,
stores_ids,
price_history_cutoff_date
)
# Only filter by categories if categories are selected
if all_category_ids:
products_query = products_query.filter(
categories__id__in=all_category_ids
).distinct()
else:
# apply location filtering and annotations if user has location
if has_user_location:
# Apply location filtering and annotations
products_query = products_query.annotate(
min_price=ProductSearchService._get_min_price_subquery(stores_ids, price_history_cutoff_date),
max_price=ProductSearchService._get_max_price_subquery(stores_ids, price_history_cutoff_date),
min_price_store_id=ProductSearchService._get_min_price_store_subquery(stores_ids, price_history_cutoff_date)
).prefetch_related("categories").filter(
prices__store__id__in=stores_ids,
prices__date__gt=price_history_cutoff_date,
prices__available=True
).distinct()
else:
# apply global filtering and annotations if user does not have location
products_query = products_query.annotate(
chains_count=Count('chains', distinct=True),
min_price=ProductSearchService._get_min_price_subquery(None, price_history_cutoff_date),
max_price=ProductSearchService._get_max_price_subquery(None, price_history_cutoff_date)
).prefetch_related("categories")
#########################################################
# Order, paginate and enrich product objects
#########################################################
# Order and paginate
products_query = products_query.order_by('name')
paginator = Paginator(products_query, 16)
page_number = request.GET.get('page', 1)
products = paginator.get_page(page_number)
# Add store information for location-based search
if has_user_location:
ProductSearchService._add_store_information(
products.object_list,
stores,
stores_ids,
price_history_cutoff_date
)
# Add follow status and cart status
ProductSearchService.add_follow_status(products.object_list, request.user)
ProductSearchService.add_cart_status(products.object_list, request)
#########################################################
# Build and Return response
#########################################################
# Get selected chains for display
selected_chains = Chain.objects.filter(id__in=selected_chain_ids)
context = {
'products': products,
'has_user_location': has_user_location,
'show_product_count_header': True,
'selected_categories': selected_categories,
'selected_category_ids': selected_category_ids,
'selected_chains': selected_chains,
'selected_chain_ids': selected_chain_ids,
'search_query': search_query,
}
template_name = 'prices/partials/catalog_search_results.html'
# if this is an HTMX request, return the rendered block
if request.htmx and not request.htmx.history_restore_request:
rendered_block = render_block_to_string(
template_name,
"catalog_browse_results_content",
request=request,
context=context,
)
response = HttpResponse(content=rendered_block)
# Disable caching for this response
response["Cache-Control"] = "no-store, max-age=0"
return response
# if this is not an HTMX request, render the full page
# check if user has location
has_user_location = LocationService.has_user_location(request)
# Get category tree with product counts
root_categories = ProductCategory.objects.filter(parent=None).order_by('name')
# Get available chains
available_chains = LocationService.get_chains_with_nearby_stores(request)
# Compute expanded category IDs (ancestors of selected categories)
expanded_category_ids = set()
if selected_category_ids:
for category in selected_categories:
# Get all ancestors for this category
ancestors = category.get_ancestors()
expanded_category_ids.update(ancestors.values_list('id', flat=True))
context.update({
'root_categories': root_categories,
'available_chains': available_chains,
'has_user_location': has_user_location,
'selected_categories': selected_categories,
'selected_category_ids': selected_category_ids,
'selected_chains': selected_chains,
'selected_chain_ids': selected_chain_ids,
'expanded_category_ids': list(expanded_category_ids),
})
response = render(request, template_name, context)
response["Cache-Control"] = "no-store, max-age=0"
return response
Editor is loading...
Leave a Comment