schon/engine/core/serializers/detail.py
Egor fureunoir Gorbunov 87ed875fe6 feat(category): add brands relationship and resolve method
enable brands association with categories and allow querying of active brands within a category. Updated GraphQL schema, models, and serializers to include this relationship while deprecating redundant category-to-brand ManyToManyField.
2026-02-21 18:27:10 +03:00

409 lines
9.5 KiB
Python

import logging
from contextlib import suppress
from typing import Any
from drf_spectacular.utils import extend_schema_field
from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.serializers import ListSerializer, ModelSerializer
from rest_framework_recursive.fields import RecursiveField
from engine.core.models import (
Attribute,
AttributeGroup,
AttributeValue,
Brand,
Category,
Feedback,
Order,
OrderProduct,
Product,
ProductImage,
ProductTag,
PromoCode,
Promotion,
Stock,
Vendor,
Wishlist,
)
from engine.core.serializers.simple import (
BrandSimpleSerializer,
CategorySimpleSerializer,
ProductSimpleSerializer,
)
from engine.core.serializers.utility import AddressSerializer
from engine.core.typing import FilterableAttribute
logger = logging.getLogger(__name__)
class AttributeGroupDetailSerializer(ModelSerializer):
children = RecursiveField(many=True)
class Meta:
model = AttributeGroup
fields = [
"uuid",
"name",
"children",
"created",
"modified",
]
class CategoryDetailListSerializer(ListSerializer):
def to_representation(self, data):
items = list(data)
with suppress(Exception):
Category.bulk_prefetch_filterable_attributes(items)
return super().to_representation(items)
class CategoryDetailSerializer(ModelSerializer):
children = SerializerMethodField()
filterable_attributes = SerializerMethodField()
brands = BrandSimpleSerializer(many=True, read_only=True)
class Meta:
model = Category
list_serializer_class = CategoryDetailListSerializer
fields = [
"uuid",
"name",
"description",
"image",
"markup_percent",
"filterable_attributes",
"brands",
"children",
"slug",
"created",
"modified",
]
def get_filterable_attributes(self, obj: Category) -> list[FilterableAttribute]:
return obj.filterable_attributes
@extend_schema_field(CategorySimpleSerializer(many=True))
def get_children(self, obj: Category) -> list[dict[str, Any]]:
request = self.context.get("request")
if request is not None and request.user.has_perm("view_category"):
children = obj.children.all()
else:
children = obj.children.filter(is_active=True)
if obj.children.exists():
serializer = CategorySimpleSerializer(
children, many=True, context=self.context
)
return list(serializer.data)
return []
class BrandDetailSerializer(ModelSerializer):
categories = CategorySimpleSerializer(many=True)
class Meta:
model = Brand
fields = [
"uuid",
"name",
"categories",
"created",
"modified",
"big_logo",
"small_logo",
]
class BrandProductDetailSerializer(ModelSerializer):
class Meta:
model = Brand
fields = [
"uuid",
"name",
"created",
"modified",
"big_logo",
"small_logo",
]
class ProductTagDetailSerializer(ModelSerializer):
class Meta:
model = ProductTag
fields = [
"uuid",
"tag_name",
"name",
"created",
"modified",
]
class ProductImageDetailSerializer(ModelSerializer):
class Meta:
model = ProductImage
fields = [
"uuid",
"alt",
"image",
"priority",
"created",
"modified",
]
class AttributeDetailSerializer(ModelSerializer):
categories = CategoryDetailSerializer(many=True)
group = AttributeGroupDetailSerializer()
class Meta:
model = Attribute
fields = [
"uuid",
"name",
"value_type",
"categories",
"group",
"created",
"modified",
]
class AttributeInnerSerializer(ModelSerializer):
group = AttributeGroupDetailSerializer()
class Meta:
model = Attribute
fields = [
"uuid",
"name",
"value_type",
"group",
"created",
"modified",
]
class AttributeValueDetailSerializer(ModelSerializer):
attribute = AttributeInnerSerializer()
class Meta:
model = AttributeValue
fields = [
"uuid",
"attribute",
"value",
"created",
"modified",
]
class VendorDetailSerializer(ModelSerializer):
class Meta:
model = Vendor
fields = [
"uuid",
"name",
"authentication",
"markup_percent",
"created",
"modified",
]
class StockDetailSerializer(ModelSerializer):
vendor = VendorDetailSerializer()
class Meta:
model = Stock
fields = [
"uuid",
"vendor",
"price",
"purchase_price",
"quantity",
"sku",
"digital_asset",
"created",
"modified",
]
class PromoCodeDetailSerializer(ModelSerializer):
class Meta:
model = PromoCode
fields = [
"uuid",
"code",
"discount_amount",
"discount_percent",
"start_time",
"end_time",
"used_on",
"created",
"modified",
]
class ProductDetailSerializer(ModelSerializer):
brand = BrandProductDetailSerializer()
category = CategorySimpleSerializer()
tags = ProductTagDetailSerializer(
many=True,
)
images = ProductImageDetailSerializer(
many=True,
)
attributes = AttributeValueDetailSerializer(
many=True,
)
rating = SerializerMethodField()
price = SerializerMethodField()
quantity = SerializerMethodField()
feedbacks_count = SerializerMethodField()
discount_price = SerializerMethodField()
personal_orders_only = SerializerMethodField()
class Meta:
model = Product
fields = [
"uuid",
"sku",
"name",
"description",
"partnumber",
"is_digital",
"brand",
"category",
"feedbacks_count",
"quantity",
"tags",
"slug",
"images",
"attributes",
"rating",
"price",
"created",
"modified",
"discount_price",
"personal_orders_only",
]
def get_rating(self, obj: Product) -> float:
return obj.rating
def get_price(self, obj: Product) -> float:
return obj.price
def get_feedbacks_count(self, obj: Product) -> int:
return obj.feedbacks_count
def get_personal_orders_only(self, obj: Product) -> bool:
return obj.personal_orders_only
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(
many=True,
)
class Meta:
model = Promotion
fields = [
"uuid",
"name",
"discount_percent",
"description",
"products",
"created",
"modified",
]
class WishlistDetailSerializer(ModelSerializer):
products = ProductSimpleSerializer(
many=True,
)
class Meta:
model = Wishlist
fields = [
"uuid",
"products",
"created",
"modified",
]
class OrderProductDetailSerializer(ModelSerializer):
product = ProductDetailSerializer()
billing_address = AddressSerializer(read_only=True, required=False)
shipping_address = AddressSerializer(read_only=True, required=False)
attributes = JSONField(required=False)
class Meta:
model = OrderProduct
fields = [
"uuid",
"product",
"quantity",
"buy_price",
"comments",
"notifications",
"billing_address",
"shipping_address",
"attributes",
"status",
"created",
"modified",
]
class FeedbackDetailSerializer(ModelSerializer):
order_product = OrderProductDetailSerializer()
class Meta:
model = Feedback
fields = [
"uuid",
"rating",
"comment",
"order_product",
"created",
"modified",
]
class OrderDetailSerializer(ModelSerializer):
promo_code = PromoCodeDetailSerializer()
order_products = OrderProductDetailSerializer(
many=True,
)
total_price = SerializerMethodField(read_only=True)
class Meta:
model = Order
fields = [
"uuid",
"status",
"total_price",
"promo_code",
"billing_address",
"shipping_address",
"buy_time",
"order_products",
"human_readable_id",
"created",
"modified",
]
def get_total_price(self, obj: Order) -> float:
return obj.total_price