schon/core/serializers/detail.py
Egor fureunoir Gorbunov 330177f6e4 Features: 1) Add # noinspection PyUnusedLocal annotations to various viewsets, filters, and migrations to suppress unnecessary warnings; 2) Improve post method in BusinessPurchaseView to handle exceptions and inactive orders gracefully; 3) Refactor resolve_transactions and related resolvers in Graphene to include more specific typing hints; 4) Include defensive coding for attributes in several models to ensure type safety.
Fixes: 1) Correct default manager assignment in `Product` model; 2) Address potential division by zero in `AbsoluteFTPStorage`; 3) Ensure proper exception handling for missing `order` attributes in CRM gateway methods; 4) Rectify inaccurate string formatting for `Transaction` `__str__` method.

Extra: Refactor various minor code style issues, including formatting corrections in the README, alignment in the emailing utility, and suppressed pycharm-specific inspections; clean up unused imports across files; enhance error messaging consistency.
2025-10-01 17:26:07 +03:00

461 lines
11 KiB
Python

import logging
from collections import defaultdict
from contextlib import suppress
from typing import Collection, Any
from django.contrib.auth.models import AnonymousUser
from django.core.cache import cache
from django.db.models.functions import Length
from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.serializers import ModelSerializer
from rest_framework_recursive.fields import RecursiveField
from core.models import (
Attribute,
AttributeGroup,
AttributeValue,
Brand,
Category,
Feedback,
Order,
OrderProduct,
Product,
ProductImage,
ProductTag,
PromoCode,
Promotion,
Stock,
Vendor,
Wishlist,
)
from core.serializers.simple import CategorySimpleSerializer, ProductSimpleSerializer
from core.serializers.utility import AddressSerializer
logger = logging.getLogger("django")
class AttributeGroupDetailSerializer(ModelSerializer):
children = RecursiveField(many=True)
class Meta:
model = AttributeGroup
fields = [
"uuid",
"name",
"children",
"created",
"modified",
]
class CategoryDetailSerializer(ModelSerializer):
children = SerializerMethodField()
image = SerializerMethodField()
filterable_attributes = SerializerMethodField()
class Meta:
model = Category
fields = [
"uuid",
"name",
"description",
"image",
"markup_percent",
"filterable_attributes",
"children",
"slug",
"created",
"modified",
]
def get_image(self, obj: Category) -> str | None:
with suppress(ValueError):
return obj.image.url
return None
def get_filterable_attributes(self, obj: Category) -> list[dict]:
cache_key = f"{obj.uuid}_filterable_results"
filterable_results = cache.get(cache_key)
if filterable_results:
return filterable_results
request = self.context.get("request")
user = getattr(request, "user", AnonymousUser())
attrs_qs = (
obj.attributes.filter(is_filterable=True)
if user.has_perm("view_attribute")
else obj.attributes.filter(is_active=True, is_filterable=True)
)
attributes = list(attrs_qs)
attr_ids = [a.id for a in attributes]
raw_vals = (
AttributeValue.objects.annotate(value_length=Length("value"))
.filter(
attribute_id__in=attr_ids,
product__category=obj,
value_length__lte=30,
)
.values_list("attribute_id", "value")
.distinct()
)
grouped = defaultdict(list)
for attr_id, val in raw_vals:
grouped[attr_id].append(val)
filterable_results = []
for attr in attributes:
vals = grouped.get(attr.id, []) # type: ignore
slice_vals = vals[:128] if len(vals) > 128 else vals
filterable_results.append(
{
"attribute_name": attr.name,
"possible_values": slice_vals,
"value_type": attr.value_type,
}
)
if not user.has_perm("view_attribute"):
cache.set(cache_key, filterable_results, 86400)
return filterable_results
def get_children(self, obj) -> Collection[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)
return CategoryDetailSerializer(children, many=True, context=self.context).data if obj.children.exists() else []
class BrandDetailSerializer(ModelSerializer):
categories = CategorySimpleSerializer(many=True)
small_logo = SerializerMethodField()
big_logo = SerializerMethodField()
class Meta:
model = Brand
fields = [
"uuid",
"name",
"categories",
"created",
"modified",
"big_logo",
"small_logo",
]
def get_small_logo(self, obj: Brand) -> str | None:
with suppress(ValueError):
return obj.small_logo.url
return None
def get_big_logo(self, obj: Brand) -> str | None:
with suppress(ValueError):
return obj.big_logo.url
return None
class BrandProductDetailSerializer(ModelSerializer):
small_logo = SerializerMethodField()
big_logo = SerializerMethodField()
class Meta:
model = Brand
fields = [
"uuid",
"name",
"created",
"modified",
"big_logo",
"small_logo",
]
def get_small_logo(self, obj: Brand) -> str | None:
with suppress(ValueError):
return obj.small_logo.url
return None
def get_big_logo(self, obj: Brand) -> str | None:
with suppress(ValueError):
return obj.big_logo.url
return None
class ProductTagDetailSerializer(ModelSerializer):
class Meta:
model = ProductTag
fields = [
"uuid",
"tag_name",
"name",
"created",
"modified",
]
class ProductImageDetailSerializer(ModelSerializer):
image = SerializerMethodField()
class Meta:
model = ProductImage
fields = [
"uuid",
"alt",
"image",
"priority",
"created",
"modified",
]
def get_image(self, obj: ProductImage) -> str:
return obj.image.url or ""
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()
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",
]
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_quantity(self, obj: Product) -> int:
return obj.quantity
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