Coverage for src/wishlists/serializers.py: 95%
22 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-08-04 12:59 +0300
« prev ^ index » next coverage.py v7.9.2, created at 2025-08-04 12:59 +0300
1from django.contrib.contenttypes.models import ContentType
2from django.db.models import Avg
4from rest_framework import serializers
6from src.wishlists.models import Wishlist
9class WishlistSerializer(serializers.ModelSerializer):
10 """
11 This serializer handles the conversion of Wishlist model instances to JSON
12 and includes additional computed fields for product information and ratings.
14 Key Features:
15 - Includes product information from the related product
16 - Calculates average rating from approved reviews
17 - Provides price range (min/max) from inventory items
18 - Determines if product is sold out based on inventory
19 - Handles GenericForeignKey relationships
20 - Provides read-only fields for computed values
21 """
23 # SlugRelatedField for content_type allows us to use the model name as a string
24 # instead of the numeric ID.
25 # The 'model' field returns the model name (e.g., 'earwear', 'necklace')
26 content_type = serializers.SlugRelatedField(
27 slug_field='model',
28 queryset=ContentType.objects.all(),
29 )
31 # SerializerMethodField for product_info allows us to include additional
32 # information about the product without modifying the model
33 product_info = serializers.SerializerMethodField()
35 class Meta:
36 model = Wishlist
37 # Fields to include in the serialized output
38 fields = [
39 'id',
40 'user',
41 'created_at',
42 'content_type',
43 'object_id',
44 'product_info',
45 ]
46 # Fields that cannot be modified through the API
47 read_only_fields = [
48 'id',
49 'created_at',
50 'user',
51 'product_info',
52 ]
54 def get_product_info(self, obj):
55 """
56 This method retrieves detailed information about the product including:
57 - Basic product details (id, images, collection, color, stone, metal)
58 - Average rating from approved reviews
59 - Price range from inventory items
60 - Sold out status based on inventory availability
61 """
62 # Get the related product object through GenericForeignKey
63 product = obj.product
65 # Calculate average rating from approved reviews only
66 # Uses Django's aggregation to get the average rating
67 # Returns 0 if no approved reviews exist
68 avg_rating = (
69 product.review.filter(
70 approved=True,
71 ).aggregate(
72 avg=Avg(
73 'rating',
74 )
75 )['avg']
76 or 0
77 )
79 # Get all inventory items for the product
80 inventory_items = product.inventory.all()
81 if inventory_items:
82 # Calculate price range from all inventory items
83 prices = [item.price for item in inventory_items]
84 min_price = min(prices)
85 max_price = max(prices)
86 else:
87 # No inventory items available
88 min_price = max_price = 0
90 # Determine if product is sold out
91 # Checks if any inventory items have quantity > 0
92 is_sold_out = not inventory_items.filter(
93 quantity__gt=0,
94 ).exists()
96 # Return comprehensive product information
97 return {
98 'id': product.id,
99 'first_image': product.first_image,
100 'second_image': product.second_image,
101 'collection__name': product.collection.name,
102 'color__name': product.color.name,
103 'stone__name': product.stone.name,
104 'metal__name': product.metal.name,
105 'is_sold_out': is_sold_out,
106 # Round to 2 decimal places
107 'average_rating': round(avg_rating, 2),
108 'min_price': min_price,
109 'max_price': max_price,
110 }