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.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-10-07 17:49:07 +03:00
parent 0752221d22
commit f646d5ab09
3 changed files with 39 additions and 49 deletions

View file

@ -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"),
]

View file

@ -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)

View file

@ -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