From f646d5ab09612b6572aaf7380b5490b95aec2844 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Tue, 7 Oct 2025 17:49:07 +0300 Subject: [PATCH] Features: 1) Add `Stock` existence filters for `has_price` and `has_stock` annotations in queries; 2) Introduce `personal_order_tail` for improved ordering logic; 3) Support advanced filtering in `DjangoFilterConnectionField` with augmented filters and types. Fixes: 1) Correct import order in multiple files; 2) Remove unnecessary migration file metadata changes. Extra: 1) Refactor ordering logic to streamline query annotations; 2) Improve code readability with explicit list annotations and consistent formatting adjustments. --- ...on_post_meta_description_ar_ar_and_more.py | 1 - core/filters.py | 44 ++++++++++--------- core/graphene/schema.py | 43 +++++++----------- 3 files changed, 39 insertions(+), 49 deletions(-) diff --git a/blog/migrations/0006_post_meta_description_post_meta_description_ar_ar_and_more.py b/blog/migrations/0006_post_meta_description_post_meta_description_ar_ar_and_more.py index 25fae75f..582bea7e 100644 --- a/blog/migrations/0006_post_meta_description_post_meta_description_ar_ar_and_more.py +++ b/blog/migrations/0006_post_meta_description_post_meta_description_ar_ar_and_more.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ("blog", "0005_post_content_fa_ir_post_content_he_il_and_more"), ] diff --git a/core/filters.py b/core/filters.py index 7b3e66ac..ac71ee02 100644 --- a/core/filters.py +++ b/core/filters.py @@ -5,10 +5,10 @@ import uuid from django.core.exceptions import BadRequest from django.db.models import ( Avg, - BooleanField, Case, Exists, FloatField, + IntegerField, Max, OuterRef, Prefetch, @@ -33,7 +33,7 @@ from django_filters import ( ) from core.elasticsearch import process_query -from core.models import Address, Brand, Category, Feedback, Order, Product, Wishlist +from core.models import Address, Brand, Category, Feedback, Order, Product, Stock, Wishlist logger = logging.getLogger("django") @@ -276,41 +276,43 @@ class ProductFilter(FilterSet): ordering_param = self.data.get("order_by", "") + stock_with_price = Stock.objects.filter(product_uuid=OuterRef("pk"), price__gt=0) + stock_with_qty = Stock.objects.filter(product_uuid=OuterRef("pk"), quantity__gt=0) + qs = qs.annotate( - has_stock=Case( - When(stocks__quantity__gt=0, then=Value(True)), - default=Value(False), - output_field=BooleanField(), - ), - has_price=Case( - When(stocks__price__gt=0, then=Value(True)), - default=Value(False), - output_field=BooleanField(), - ), + has_price=Exists(stock_with_price), + has_stock=Exists(stock_with_qty), ).annotate( - personal_orders_only=Case( - When(has_stock=False, has_price=False, then=Value(True)), - default=Value(False), - output_field=BooleanField(), + personal_order_tail=Case( + When(Q(has_price=False) | Q(has_stock=False), then=Value(1)), + default=Value(0), + output_field=IntegerField(), ) ) requested = [part.strip() for part in ordering_param.split(",") if part.strip()] if ordering_param else [] - mapped_requested = [] + mapped_requested: list[str] = [] + for part in requested: desc = part.startswith("-") key = part.lstrip("-") + if key == "price": key = "price_order" + if key == "random": - key = "?" - mapped_requested.append(key) + mapped_requested.append("?") continue - if key == "personal_orders_only": + + if key in {"personal_orders_only", "personal_order_only", "personal_order_tail"}: continue + mapped_requested.append(f"-{key}" if desc else key) - final_ordering = mapped_requested + ["personal_orders_only"] + if "?" in mapped_requested: + final_ordering = ["personal_order_tail", "?"] + else: + final_ordering = mapped_requested + ["personal_order_tail"] if final_ordering: qs = qs.order_by(*final_ordering) diff --git a/core/graphene/schema.py b/core/graphene/schema.py index 5382cb66..a1dbe3ea 100644 --- a/core/graphene/schema.py +++ b/core/graphene/schema.py @@ -1,22 +1,22 @@ import logging from django.core.cache import cache -from django.db.models import Max, Case, When, Value, IntegerField, BooleanField -from django.utils import timezone from django.core.exceptions import PermissionDenied +from django.db.models import Case, Exists, IntegerField, OuterRef, Q, Value, When +from django.utils import timezone from graphene import Field, List, ObjectType, Schema from graphene_django.filter import DjangoFilterConnectionField from blog.filters import PostFilter from blog.graphene.object_types import PostType from core.filters import ( + AddressFilter, BrandFilter, CategoryFilter, FeedbackFilter, OrderFilter, ProductFilter, WishlistFilter, - AddressFilter, ) from core.graphene.mutations import ( AddOrderProduct, @@ -44,6 +44,7 @@ from core.graphene.mutations import ( UpdateProduct, ) from core.graphene.object_types import ( + AddressType, AttributeGroupType, BrandType, CategoryTagType, @@ -61,9 +62,9 @@ from core.graphene.object_types import ( StockType, VendorType, WishlistType, - AddressType, ) from core.models import ( + Address, AttributeGroup, Brand, Category, @@ -79,7 +80,6 @@ from core.models import ( Stock, Vendor, Wishlist, - Address, ) from core.utils import get_project_parameters from core.utils.languages import get_flag_by_language @@ -163,35 +163,24 @@ class Query(ObjectType): .prefetch_related("images", "stocks") ) - base_qs = ( + stock_with_price = Stock.objects.filter(product_uuid=OuterRef("pk"), price__gt=0) + stock_with_qty = Stock.objects.filter(product_uuid=OuterRef("pk"), quantity__gt=0) + + return ( base_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(), - ) - ), + has_price=Exists(stock_with_price), + has_stock=Exists(stock_with_qty), ) .annotate( - personal_order_only=Case( - When(has_stock=0, has_price=1, then=Value(True)), - default=Value(False), - output_field=BooleanField(), + personal_order_tail=Case( + When(Q(has_price=False) | Q(has_stock=False), then=Value(1)), + default=Value(0), + output_field=IntegerField(), ) ) - .order_by("personal_order_only") + .order_by("personal_order_tail") ) - return base_qs - @staticmethod def resolve_orders(_parent, info, **kwargs): orders = Order.objects