From 91b043ac4fea73a84bc645d5635155b6cd7b146d Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Tue, 16 Sep 2025 15:04:59 +0300 Subject: [PATCH 1/3] Features: 1) Add `personal_order_only` ordering option; 2) Annotate queryset with `personal_order_only` calculated field; 3) Support custom ordering for `price` and `personal_order_only`. Fixes: 1) Correct `rating` ordering logic to use cleaned field names without signs. Extra: 1) Add missing import for `BooleanField`; 2) Refactor ordering parameter processing for clarity and efficiency. --- core/filters.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/core/filters.py b/core/filters.py index 3122fdf9..6c57a58f 100644 --- a/core/filters.py +++ b/core/filters.py @@ -16,6 +16,7 @@ from django.db.models import ( When, Max, Prefetch, + BooleanField, ) from django.db.models.functions import Coalesce from django.utils.http import urlsafe_base64_decode @@ -92,6 +93,7 @@ class ProductFilter(FilterSet): ("price_order", "price"), ("sku", "sku"), ("?", "random"), + ("personal_order_only", "personal_order_only"), ), initial="uuid", ) @@ -279,8 +281,8 @@ class ProductFilter(FilterSet): ordering_param = self.data.get("order_by", "") if ordering_param: - order_fields = [field.strip() for field in ordering_param.split(",")] - if any(field.lstrip("-") == "rating" for field in order_fields): + order_fields_no_sign = [field.strip("-") for field in ordering_param.split(",")] + if "rating" in order_fields_no_sign: feedback_qs = ( Feedback.objects.filter(order_product__product_id=OuterRef("pk")) .values("order_product__product_id") @@ -293,6 +295,43 @@ class ProductFilter(FilterSet): Value(0, output_field=FloatField()), ) ) + + if ordering_param and any(f.lstrip("-") == "price" for f in ordering_param.split(",")): + qs = qs.annotate( + price_order=Coalesce( + Max("stocks__price"), + Value(0.0), + output_field=FloatField(), + ) + ) + + qs = qs.annotate( + personal_order_only=Case( + When(has_stock=Value(0), has_price=Value(1), then=Value(True)), + default=Value(False), + output_field=BooleanField(), + ) + ) + + requested = [part.strip() for part in ordering_param.split(",") if part.strip()] if ordering_param else [] + mapped_requested = [] + for part in requested: + desc = part.startswith("-") + key = part.lstrip("-") + if key == "price": + key = "price_order" + if key == "random": + key = "?" + mapped_requested.append(key) + continue + mapped_requested.append(f"-{key}" if desc else key) + + has_personal_in_request = any(p.lstrip("-") == "personal_order_only" for p in mapped_requested) + final_ordering = (["personal_order_only"] if not has_personal_in_request else []) + mapped_requested + + if final_ordering: + qs = qs.order_by(*final_ordering) + return qs.distinct() From fe6316dae137fa060c001ad786895c868a58409e Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Tue, 16 Sep 2025 15:46:25 +0300 Subject: [PATCH 2/3] Features: 1) Add `has_stock` and `has_price` annotations for stock and price availability checks. Fixes: 1) Correct conditional logic in `personal_order_only` annotation to use integers for comparison. Extra: 1) Minor readability improvements in annotation definitions. --- core/filters.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/core/filters.py b/core/filters.py index 6c57a58f..d9706a30 100644 --- a/core/filters.py +++ b/core/filters.py @@ -306,8 +306,23 @@ class ProductFilter(FilterSet): ) qs = qs.annotate( + has_stock=Max( + Case( + When(stocks__quantity__gt=0, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ), + has_price=Max( + Case( + When(stocks__price__gt=0, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ), + ).annotate( personal_order_only=Case( - When(has_stock=Value(0), has_price=Value(1), then=Value(True)), + When(has_stock=0, has_price=1, then=Value(True)), default=Value(False), output_field=BooleanField(), ) From 018d8ca8e04184bb038d32875b5e2ab0e0d76428 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Wed, 17 Sep 2025 18:58:07 +0300 Subject: [PATCH 3/3] Features: 1) Add `rating` field to `Product` object with description and GraphQL schema; 2) Implement `resolve_rating` to fetch the rating value for `Product`; Fixes: None; Extra: 1) Update field lists in `Product` meta to include `rating`. --- core/graphene/object_types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/graphene/object_types.py b/core/graphene/object_types.py index 1106fd63..d0e60f40 100644 --- a/core/graphene/object_types.py +++ b/core/graphene/object_types.py @@ -501,6 +501,7 @@ class ProductType(DjangoObjectType): feedbacks_count = Int(description=_("number of feedbacks")) personal_orders_only = Boolean(description=_("only available for personal orders")) seo_meta = Field(SEOMetaType, description=_("SEO Meta snapshot")) + rating = Float(description=_("rating value from 1 to 10, inclusive, or 0 if not set.")) class Meta: model = Product @@ -521,6 +522,7 @@ class ProductType(DjangoObjectType): "attribute_groups", "images", "price", + "rating", ) filter_fields = ["uuid", "name"] description = _("products") @@ -528,6 +530,9 @@ class ProductType(DjangoObjectType): def resolve_price(self: Product, _info) -> float: return self.price or 0.0 + def resolve_rating(self: Product, _info) -> float: + return self.rating or 0.0 + def resolve_feedbacks(self: Product, _info): if _info.context.user.has_perm("core.view_feedback"): return Feedback.objects.filter(order_product__product=self)