From 753a2059d45718381f619cc95e60110f690d3825 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Sun, 26 Oct 2025 19:51:41 +0300 Subject: [PATCH] Features: 1) Add `discount_price` field to `ProductDetailSerializer`, `PromotionDetailSerializer`, and `Product` GraphQL type; 2) Introduce `promos` and `discount_price` properties in the `Product` model; Fixes: None; Extra: 1) Update field inclusion lists and methods in serializers to reflect the new `discount_price` field; 2) Add model property caching for `discount_price`; --- core/graphene/object_types.py | 4 ++++ core/models.py | 11 +++++++++++ core/serializers/detail.py | 5 +++++ core/serializers/simple.py | 5 +++++ 4 files changed, 25 insertions(+) diff --git a/core/graphene/object_types.py b/core/graphene/object_types.py index 4767a948..cb147101 100644 --- a/core/graphene/object_types.py +++ b/core/graphene/object_types.py @@ -481,6 +481,7 @@ class ProductType(DjangoObjectType): # type: ignore [misc] personal_orders_only = Boolean(description=_("only available for personal orders")) seo_meta = Field(SEOMetaType, description=_("SEO Meta snapshot")) rating = Float(description=_("rating value from 1 to 10, inclusive, or 0 if not set.")) + discount_price = Float(description=_("discount price")) class Meta: model = Product @@ -577,6 +578,9 @@ class ProductType(DjangoObjectType): # type: ignore [misc] "hreflang": info.context.LANGUAGE_CODE, } + def resolve_discount_price(self: Product, _info) -> float | None: + return self.discount_price + class AttributeValueType(DjangoObjectType): # type: ignore [misc] value = String(description=_("attribute value")) diff --git a/core/models.py b/core/models.py index b539c011..6d4bcedf 100644 --- a/core/models.py +++ b/core/models.py @@ -658,6 +658,17 @@ class Product(ExportModelOperationsMixin("product"), NiceModel): # type: ignore def __str__(self): return self.name + @property + def promos(self) -> QuerySet["Promotion"]: + return Promotion.objects.filter( + is_active=True, + products__in=[self.pk], + ).order_by("-discount_percent") + + @cached_property + def discount_price(self) -> float | None: + return self.promos.first().discount_percent if self.promos.exists() else None # type: ignore [union-attr] + @property def rating(self) -> float: feedbacks = Feedback.objects.filter(order_product__product_id=self.pk) diff --git a/core/serializers/detail.py b/core/serializers/detail.py index e3d141db..ad1fc9e5 100644 --- a/core/serializers/detail.py +++ b/core/serializers/detail.py @@ -286,6 +286,7 @@ class ProductDetailSerializer(ModelSerializer): price = SerializerMethodField() quantity = SerializerMethodField() feedbacks_count = SerializerMethodField() + discount_price = SerializerMethodField() class Meta: model = Product @@ -308,6 +309,7 @@ class ProductDetailSerializer(ModelSerializer): "price", "created", "modified", + "discount_price", ] def get_rating(self, obj: Product) -> float: @@ -322,6 +324,9 @@ class ProductDetailSerializer(ModelSerializer): def get_quantity(self, obj: Product) -> int: return obj.quantity + def get_discount_price(self, obj: Product) -> float | None: + return obj.discount_price + class PromotionDetailSerializer(ModelSerializer): products = ProductDetailSerializer( diff --git a/core/serializers/simple.py b/core/serializers/simple.py index d9b4f157..ae1e4144 100644 --- a/core/serializers/simple.py +++ b/core/serializers/simple.py @@ -157,6 +157,7 @@ class ProductSimpleSerializer(ModelSerializer): # type: ignore [type-arg] quantity = SerializerMethodField() feedbacks_count = SerializerMethodField() personal_orders_only = SerializerMethodField() + discount_price = SerializerMethodField() class Meta: model = Product @@ -178,6 +179,7 @@ class ProductSimpleSerializer(ModelSerializer): # type: ignore [type-arg] "rating", "price", "quantity", + "discount_price", ] def get_rating(self, obj: Product) -> float: @@ -195,6 +197,9 @@ class ProductSimpleSerializer(ModelSerializer): # type: ignore [type-arg] def get_personal_orders_only(self, obj: Product) -> bool: return obj.personal_orders_only + def get_discount_price(self, obj: Product) -> float | None: + return obj.discount_price + class VendorSimpleSerializer(ModelSerializer): # type: ignore [type-arg] class Meta: