Untitled

 avatar
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