This commit introduces support for uploading optional video files to products and image files to blog posts. Enhanced admin interfaces were added to preview these files directly. Also includes adjustments to GraphQL types and serializers to expose the new fields.
438 lines
10 KiB
Python
438 lines
10 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
|
|
from engine.core.utils.markdown import render_markdown
|
|
|
|
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()
|
|
description = SerializerMethodField()
|
|
filterable_attributes = SerializerMethodField()
|
|
brands = BrandSimpleSerializer(many=True, read_only=True)
|
|
min_price = SerializerMethodField()
|
|
max_price = SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Category
|
|
list_serializer_class = CategoryDetailListSerializer
|
|
fields = [
|
|
"uuid",
|
|
"name",
|
|
"description",
|
|
"image",
|
|
"markup_percent",
|
|
"filterable_attributes",
|
|
"brands",
|
|
"min_price",
|
|
"max_price",
|
|
"children",
|
|
"slug",
|
|
"created",
|
|
"modified",
|
|
]
|
|
|
|
def get_description(self, obj: Category) -> str:
|
|
return render_markdown(obj.description or "")
|
|
|
|
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 []
|
|
|
|
def get_min_price(self, obj: Category) -> float:
|
|
return obj.min_price
|
|
|
|
def get_max_price(self, obj: Category) -> float:
|
|
return obj.max_price
|
|
|
|
|
|
class BrandDetailSerializer(ModelSerializer):
|
|
categories = CategorySimpleSerializer(many=True)
|
|
description = SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Brand
|
|
fields = [
|
|
"uuid",
|
|
"name",
|
|
"description",
|
|
"categories",
|
|
"created",
|
|
"modified",
|
|
"big_logo",
|
|
"small_logo",
|
|
]
|
|
|
|
def get_description(self, obj: Brand) -> str:
|
|
return render_markdown(obj.description or "")
|
|
|
|
|
|
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,
|
|
)
|
|
|
|
description = SerializerMethodField()
|
|
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",
|
|
"video",
|
|
"partnumber",
|
|
"is_digital",
|
|
"brand",
|
|
"category",
|
|
"feedbacks_count",
|
|
"quantity",
|
|
"tags",
|
|
"slug",
|
|
"images",
|
|
"attributes",
|
|
"rating",
|
|
"price",
|
|
"created",
|
|
"modified",
|
|
"discount_price",
|
|
"personal_orders_only",
|
|
]
|
|
|
|
def get_description(self, obj: Product) -> str:
|
|
return render_markdown(obj.description or "")
|
|
|
|
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,
|
|
)
|
|
description = SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Promotion
|
|
fields = [
|
|
"uuid",
|
|
"name",
|
|
"discount_percent",
|
|
"description",
|
|
"products",
|
|
"created",
|
|
"modified",
|
|
]
|
|
|
|
def get_description(self, obj: Promotion) -> str:
|
|
return render_markdown(obj.description or "")
|
|
|
|
|
|
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
|