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.
This commit is contained in:
Egor Pavlovich Gorbunov 2026-03-01 22:22:59 +03:00
parent ce689ee754
commit ff99177139
3 changed files with 69 additions and 1 deletions

View file

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

View file

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

View file

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