From 7b60cf3d6d88b0271c8773847483a559d0f8de87 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Sun, 8 Jun 2025 16:42:18 +0300 Subject: [PATCH] Fixes: ORDER_SCHEMA --- core/docs/drf/viewsets.py | 55 ++++++++++++++++++++++++++++++++++ core/filters.py | 62 ++++++++++++++++++++++++++++++++++----- core/managers.py | 2 +- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/core/docs/drf/viewsets.py b/core/docs/drf/viewsets.py index b2e99959..c5305431 100644 --- a/core/docs/drf/viewsets.py +++ b/core/docs/drf/viewsets.py @@ -149,8 +149,63 @@ ORDER_SCHEMA = { "list": extend_schema( summary=_("list all orders (simple view)"), description=_("for non-staff users, only their own orders are returned."), + parameters=[ + OpenApiParameter( + name="search", + type=OpenApiTypes.STR, + description=_( + "Case-insensitive substring search across human_readable_id, " + "order_products.product.name, and order_products.product.partnumber" + ), + ), + OpenApiParameter( + name="min_buy_time", + type=OpenApiTypes.DATETIME, + description=_("Filter orders with buy_time >= this ISO 8601 datetime"), + ), + OpenApiParameter( + name="max_buy_time", + type=OpenApiTypes.DATETIME, + description=_("Filter orders with buy_time <= this ISO 8601 datetime"), + ), + OpenApiParameter( + name="uuid", + type=OpenApiTypes.UUID, + description=_("Filter by exact order UUID"), + ), + OpenApiParameter( + name="human_readable_id", + type=OpenApiTypes.STR, + description=_("Filter by exact human-readable order ID"), + ), + OpenApiParameter( + name="user_email", + type=OpenApiTypes.STR, + description=_("Filter by user's email (case-insensitive exact match)"), + ), + OpenApiParameter( + name="user", + type=OpenApiTypes.UUID, + description=_("Filter by user's UUID"), + ), + OpenApiParameter( + name="status", + type=OpenApiTypes.STR, + description=_("Filter by order status (case-insensitive substring match)"), + ), + OpenApiParameter( + name="order_by", + type=OpenApiTypes.STR, + description=_( + "Order by one of: uuid, human_readable_id, user_email, user, " + "status, created, modified, buy_time, random. " + "Prefix with '-' for descending (e.g. '-buy_time')." + ), + ), + ], responses={status.HTTP_200_OK: OrderSimpleSerializer(many=True), **BASE_ERRORS}, ), + # ... other actions unchanged "retrieve": extend_schema( summary=_("retrieve a single order (detailed view)"), responses={status.HTTP_200_OK: OrderDetailSerializer(), **BASE_ERRORS}, diff --git a/core/filters.py b/core/filters.py index 044eb905..776d5537 100644 --- a/core/filters.py +++ b/core/filters.py @@ -5,7 +5,16 @@ import uuid from django.db.models import Avg, FloatField, OuterRef, Q, Subquery, Value from django.db.models.functions import Coalesce from django.utils.http import urlsafe_base64_decode -from django_filters import BaseInFilter, BooleanFilter, CharFilter, FilterSet, NumberFilter, OrderingFilter, UUIDFilter +from django_filters import ( + BaseInFilter, + BooleanFilter, + CharFilter, + DateTimeFilter, + FilterSet, + NumberFilter, + OrderingFilter, + UUIDFilter, +) from core.models import Brand, Category, Feedback, Order, Product, Wishlist @@ -210,11 +219,27 @@ class ProductFilter(FilterSet): class OrderFilter(FilterSet): - uuid = UUIDFilter(field_name="uuid", lookup_expr="exact") - user_email = CharFilter(field_name="user__email", lookup_expr="iexact") - user = UUIDFilter(field_name="user__uuid", lookup_expr="exact") - status = CharFilter(field_name="status", lookup_expr="icontains", label="Status") - human_readable_id = CharFilter(field_name="human_readable_id", lookup_expr="exact") + search = CharFilter( + method='filter_search', + label='Search (ID, product name or part number)', + ) + + min_buy_time = DateTimeFilter( + field_name='buy_time', + lookup_expr='gte', + label='Bought after (inclusive)' + ) + max_buy_time = DateTimeFilter( + field_name='buy_time', + lookup_expr='lte', + label='Bought before (inclusive)' + ) + + uuid = UUIDFilter(field_name='uuid', lookup_expr='exact') + user_email = CharFilter(field_name='user__email', lookup_expr='iexact') + user = UUIDFilter(field_name='user__uuid', lookup_expr='exact') + status = CharFilter(field_name='status', lookup_expr='icontains', label='Status') + human_readable_id = CharFilter(field_name='human_readable_id', lookup_expr='exact') order_by = OrderingFilter( fields=( @@ -232,7 +257,28 @@ class OrderFilter(FilterSet): class Meta: model = Order - fields = ["uuid", "human_readable_id", "user_email", "user", "status", "order_by"] + fields = [ + "uuid", + "human_readable_id", + "user_email", + "user", + "status", + "order_by", + "search", + "min_buy_time", + "max_buy_time", + ] + + def filter_search(self, queryset, _name, value): + return ( + queryset + .filter( + Q(human_readable_id__icontains=value) | + Q(order_products__product__name__icontains=value) | + Q(order_products__product__partnumber__icontains=value) + ) + .distinct() + ) class WishlistFilter(FilterSet): @@ -267,7 +313,7 @@ class CategoryFilter(FilterSet): model = Category fields = ["uuid", "name", "parent_uuid", "slug"] - def filter_parent_uuid(self, queryset, name, value): + def filter_parent_uuid(self, queryset, _name, value): """ If ?parent_uuid= or ?parent_uuid=null, return items with parent=None. Otherwise treat `value` as a real UUID and filter parent__uuid=value. diff --git a/core/managers.py b/core/managers.py index 115f2e99..2eab6e71 100644 --- a/core/managers.py +++ b/core/managers.py @@ -70,6 +70,6 @@ class AddressManager(models.Manager): region=region, postal_code=postal_code, country=country, + user=kwargs.pop('user'), defaults={"api_response": data, "location": location}, - **kwargs, )[0]