from .models import * from rest_framework import serializers from django.utils import timezone from datetime import timedelta from django.contrib.auth.models import AnonymousUser class DetailSerializer(serializers.ModelSerializer): texts = serializers.SerializerMethodField() class Meta: model = DetailModel exclude = ['detail_model', 'detail_text1', 'detail_text2', 'detail_text3', 'detail_text4'] def get_texts(self, obj): return [ text for text in [ obj.detail_text1, obj.detail_text2, obj.detail_text3, obj.detail_text4, ] if text ] class ProductDetailSerializer(serializers.ModelSerializer): details = DetailSerializer(many=True, read_only=True) detail_category = serializers.StringRelatedField() class Meta: model = ProductDetailModel exclude = ['name'] class AttributeTypeSerialzier(serializers.ModelSerializer): class Meta: model = AttributeType fields = "__all__" class AttributeValueSerialzier(serializers.ModelSerializer): attribute_type = AttributeTypeSerialzier() class Meta: model = AttributeValue fields = "__all__" class InPackItemsSerialzier(serializers.ModelSerializer): class Meta: model = InPackItems fields = '__all__' class ProductImageSerailizer(serializers.ModelSerializer): class Meta: model = ProductImageModel fields = '__all__' class ProductVariantSerialzier(serializers.ModelSerializer): product_attributes = AttributeValueSerialzier(many=True) in_pack_items = InPackItemsSerialzier(many=True) images = ProductImageSerailizer(many=True) details = ProductDetailSerializer(many=True, read_only=True) cart_quantity = serializers.SerializerMethodField() price = serializers.SerializerMethodField() price_after_discount = serializers.SerializerMethodField() special_discount_amount = serializers.SerializerMethodField() special_discount_link = serializers.SerializerMethodField() class Meta: model = ProductVariant exclude = ('min_price', 'sell', 'currency', 'product', 'input_price', 'price_in_dollor', 'profit', 'special_discount_percent') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) view_type = self.context.get('view_type', None) if view_type == 'list': self.fields.pop('in_pack_items', None) def get_special_discount_amount(self, obj): if obj.special_discount_percent: special_discount_amount = obj.profit * (obj.special_discount_percent / 100) return f'{special_discount_amount:,.0f} تومانءءء' else: return None def get_special_discount_link(self, obj): request = self.context.get('request') if request.user.is_authenticated: return 'https://google.com' else: return None def get_price_after_discount(self, obj): return f'{obj.price_after_discount:,.0f} تومانءءء' def get_cart_quantity(self, obj): request = self.context.get('request') if not request or not request.user.is_authenticated: return 0 cart_items = self.context.get('cart_items', []) if cart_items: for item in cart_items: if item['product']['id'] == obj.id: return item['quantity'] return 0 def get_price(self, obj): return f'{obj.price:,.0f} تومانءءء' class SubCategorySerializer(serializers.ModelSerializer): product_count = serializers.SerializerMethodField() parent = serializers.SerializerMethodField() class Meta: model = SubCategoryModel fields = ['id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'product_count', 'parent', 'image'] def get_product_count(self, obj): # Use annotated product_count from database annotation return getattr(obj, 'product_count', 0) def get_parent(self, obj): return obj.parent.name if obj.parent else None class UnitCategorySerializer(serializers.ModelSerializer): class Meta: model = UnitCategoryModel fields = ['id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'image'] class MainCategorySerializer(serializers.ModelSerializer): subcategorys = SubCategorySerializer(many=True) class Meta: model = MainCategoryModel fields = ['id', 'name', 'slug', 'icon', 'meta_title', 'meta_description', 'subcategorys', 'image', 'video'] class DynamicProductSerializer(serializers.ModelSerializer): variants = serializers.SerializerMethodField() colors = serializers.SerializerMethodField() category = SubCategorySerializer(read_only=True) is_new = serializers.SerializerMethodField() related_products = serializers.SerializerMethodField() added_to_favorites = serializers.SerializerMethodField() best_deal_price_before_discount = serializers.SerializerMethodField() best_deal_price_after_discount = serializers.SerializerMethodField() best_deal_discount = serializers.SerializerMethodField() main_image = serializers.SerializerMethodField() customer_pickup_title = serializers.SerializerMethodField() customer_pickup_description = serializers.SerializerMethodField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) view_type = self.context.get('view_type', 'all') if view_type != 'all': allowed_fields = self.Meta.view_type[view_type] allowed = set(allowed_fields) existing = set(self.fields.keys()) for field_name in existing - allowed: self.fields.pop(field_name) class Meta: model = ProductModel fields = "__all__" view_type = { 'list': ['id', 'name', 'rating', 'slug', 'category', 'colors', 'image', 'best_deal_price_before_discount', 'best_deal_price_after_discount', 'best_deal_discount', 'main_image'], 'slider': ['id', 'name', 'rating', 'slug', 'category', 'variants', 'colors', 'image', 'best_deal_price_before_discount', 'best_deal_price_after_discount', 'best_deal_discount', ], 'instance': ['id', 'name', 'description', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'in_pack_items', 'variants', 'colors', 'added_to_favorites', 'image', 'customer_pickup_title', 'customer_pickup_description'], 'chat': ['id', 'name', 'description', 'variants', 'image'] } def _get_best_deal_variant(self, obj): """Get best deal variant from prefetched variants (pre-ordered by discount/price)""" if not hasattr(self, '_best_deal_cache'): self._best_deal_cache = {} if obj.id not in self._best_deal_cache: # Get first from prefetched variants (already ordered by discount desc, price asc) variants = list(obj.variants.all()) self._best_deal_cache[obj.id] = variants[0] if variants else None return self._best_deal_cache[obj.id] def get_main_image(self, obj): if obj.image: return obj.image.url # Use prefetched variants variants = list(obj.variants.all()) if variants: images = list(variants[0].images.all()) if images: return images[0].image.url return None def get_best_deal_price_before_discount(self, obj): best_deal = self._get_best_deal_variant(obj) if best_deal: return f'{best_deal.price:,.0f} تومانءءء' return 0 def get_best_deal_price_after_discount(self, obj): best_deal = self._get_best_deal_variant(obj) if best_deal: price_after_discount = best_deal.price_after_discount return f'{price_after_discount:,.0f} تومانءءء' return 0 def get_best_deal_discount(self, obj): best_deal = self._get_best_deal_variant(obj) if best_deal: return best_deal.discount return 0 def get_customer_pickup_title(self, obj): if obj.shop: return obj.shop.customer_pickup_title return None def get_customer_pickup_description(self, obj): if obj.shop: return obj.shop.customer_pickup_description return None def get_added_to_favorites(self, obj): from account.models import UserFavorites request = self.context.get('request') if not request or not request.user.is_authenticated: return False # Use exists() with filter instead of fetching all products return UserFavorites.objects.filter(user=request.user, products=obj).exists() def get_variants(self, obj): view_type = self.context.get('view_type') if view_type == 'slider': varients = obj.variants.filter(slider_category__isnull=False) else: varients = obj.variants.all() return ProductVariantSerialzier(instance=varients, many=True, context=self.context).data def get_colors(self, obj): # Get colors from prefetched variants efficiently variants = list(obj.variants.all()) colors = set() for variant in variants: if variant.color: colors.add(variant.color) return list(colors) def get_is_new(self, obj): return timezone.now() < obj.created_at + timedelta(days=7) def get_related_products(self, obj): if obj.related_products.all().count() >= 5: related_products = obj.related_products.all()[:10] else: related_products = obj.category.products.exclude(id=obj.id)[:10] serializer = DynamicProductSerializer( related_products, many=True, context={ 'view_type': 'list', 'request': self.context.get('request') } ) return serializer.data class CommentSerializer(serializers.ModelSerializer): class Meta: model = CommentModel exclude = ('review_status', ) read_only_fields = ('review_status', 'product', 'user') class BotProductSerializer(serializers.ModelSerializer): class Meta: model = ProductModel fields = [ 'pk', 'name' ]