From ff99177139cc4b7a9d68d103024541b31211f4c2 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Sun, 1 Mar 2026 22:22:59 +0300 Subject: [PATCH] feat(viewsets): add endpoint to retrieve products by identifier introduce `exact_list` action in `viewsets.py` to retrieve products by `uuid`, `slug`, or `sku` identifiers. Includes input validation and ratelimiting. Adds corresponding schema documentation and a GraphQL mutation for similar functionality. --- engine/core/docs/drf/viewsets.py | 14 ++++++++++++++ engine/core/graphene/mutations.py | 28 +++++++++++++++++++++++++++- engine/core/viewsets.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/engine/core/docs/drf/viewsets.py b/engine/core/docs/drf/viewsets.py index 5d7e0a67..eb6b0086 100644 --- a/engine/core/docs/drf/viewsets.py +++ b/engine/core/docs/drf/viewsets.py @@ -750,6 +750,20 @@ PRODUCT_SCHEMA = { **BASE_ERRORS, }, ), + "exact_list": extend_schema( + tags=[ + "products", + ], + summary=_("retrieve exact products by identifier"), + description=_( + "retrieve a list of products by identifier type (uuid, slug, or sku). " + "Send a POST request with `identificator_type` and `identificators` (list of values)." + ), + responses={ + status.HTTP_200_OK: ProductSimpleSerializer(many=True), + **BASE_ERRORS, + }, + ), "seo_meta": extend_schema( tags=[ "products", diff --git a/engine/core/graphene/mutations.py b/engine/core/graphene/mutations.py index eca0f2c3..a0b82b4f 100644 --- a/engine/core/graphene/mutations.py +++ b/engine/core/graphene/mutations.py @@ -15,10 +15,11 @@ from engine.core.graphene.object_types import ( BulkProductInput, FeedbackType, OrderType, + ProductType, SearchResultsType, WishlistType, ) -from engine.core.models import Address, Order, OrderProduct, Wishlist +from engine.core.models import Address, Order, OrderProduct, Product, Wishlist from engine.core.utils import format_attributes, is_url_safe from engine.core.utils.caching import web_cache from engine.core.utils.emailing import contact_us_email @@ -574,6 +575,31 @@ class BuyProduct(Mutation): ) +class RetrieveExactProducts(Mutation): + class Meta: + description = _("retrieve exact products by identificator") + + class Arguments: + identificator_type = String(required=True) + identificators = List(String, required=True) + + products = List(ProductType, required=True) + + def mutate(self, info, identificator_type: str, identificators: list[str]): + match identificator_type: + case "uuid": + products = Product.objects.filter(uuid__in=identificators) + case "slug": + products = Product.objects.filter(slug__in=identificators) + case "sku": + products = Product.objects.filter(sku__in=identificators) + case _: + raise BadRequest( + _("identificator_type must be one of: uuid, slug, sku") + ) + return RetrieveExactProducts(products=products) # ty: ignore[unknown-argument] + + # noinspection PyUnusedLocal,PyTypeChecker class FeedbackProductAction(Mutation): class Meta: diff --git a/engine/core/viewsets.py b/engine/core/viewsets.py index 4b43373e..4e2a501a 100644 --- a/engine/core/viewsets.py +++ b/engine/core/viewsets.py @@ -530,6 +530,34 @@ class ProductViewSet(SchonViewSet): self.check_object_permissions(self.request, obj) return obj + @action(detail=False, methods=("POST",), url_path="retrieve-exact") + @method_decorator(ratelimit(key="ip", rate="8/s" if not settings.DEBUG else "44/s")) + def exact_list(self, request: Request, *args, **kwargs) -> Response: + identificator_type = request.data.get("identificator_type") + identificators = request.data.get("identificators", []) + + if not identificator_type or not identificators: + return Response( + {"detail": _("identificator_type and identificators are required")}, + status=status.HTTP_400_BAD_REQUEST, + ) + + match identificator_type: + case "uuid": + qs = self.get_queryset().filter(uuid__in=identificators) + case "slug": + qs = self.get_queryset().filter(slug__in=identificators) + case "sku": + qs = self.get_queryset().filter(sku__in=identificators) + case _: + return Response( + {"detail": _("identificator_type must be one of: uuid, slug, sku")}, + status=status.HTTP_400_BAD_REQUEST, + ) + + serializer = ProductSimpleSerializer(qs, many=True) + return Response(serializer.data) + # noinspection PyUnusedLocal @action(detail=True, methods=("GET",), url_path="feedbacks") @method_decorator(ratelimit(key="ip", rate="2/s" if not settings.DEBUG else "44/s"))