Coverage for src/products/models/review.py: 96%

23 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-08-04 12:59 +0300

1from django.db import models 

2from django.contrib.auth import get_user_model 

3from django.contrib.contenttypes.models import ContentType 

4from django.contrib.contenttypes.fields import GenericForeignKey 

5from django.core.validators import MaxLengthValidator 

6 

7from src.products.constants import ReviewFieldLengths 

8 

9User = get_user_model() 

10 

11 

12class Review(models.Model): 

13 """ 

14 Review model for user feedback on products. 

15 

16 This model allows users to rate and comment on any product in the system. 

17 It uses Django's GenericForeignKey to work with different product types 

18 (Earwear, Neckwear, Fingerwear, Wristwear) without needing separate 

19 review tables for each category. 

20 

21 The model includes an approval system where reviews must be approved 

22 before being displayed publicly, allowing for content moderation. 

23 

24 Key features: 

25 - 1-5 star rating system 

26 - Text comments with length limits 

27 - Approval workflow for moderation 

28 - Generic relationship to any product type 

29 - User association for accountability 

30 """ 

31 

32 RATING_CHOICES = [(i, str(i)) for i in range(1, 6)] 

33 

34 class Meta: 

35 ordering = ['-created_at'] 

36 

37 # Ensure each user can only review each product once 

38 unique_together = ( 

39 'user', 

40 'content_type', 

41 'object_id', 

42 ) 

43 

44 # Custom permission for approving reviews 

45 # This allows admin to control who can approve reviews 

46 permissions = [ 

47 ( 

48 'approve_review', 

49 'Can approve reviews', 

50 ), 

51 ] 

52 

53 rating = models.IntegerField( 

54 choices=RATING_CHOICES, 

55 ) 

56 

57 comment = models.TextField( 

58 validators=[ 

59 MaxLengthValidator( 

60 ReviewFieldLengths.MAX_COMMENT_LENGTH, 

61 ), 

62 ], 

63 ) 

64 

65 # Only approved reviews are displayed publicly 

66 approved = models.BooleanField( 

67 default=False, 

68 ) 

69 

70 created_at = models.DateTimeField( 

71 auto_now_add=True, 

72 ) 

73 

74 # GenericForeignKey components for flexible product relationships 

75 # content_type stores which model the review is for (Earwear, Neckwear, etc.) 

76 content_type = models.ForeignKey( 

77 ContentType, 

78 on_delete=models.CASCADE, 

79 ) 

80 

81 # object_id stores the specific product's ID 

82 object_id = models.PositiveIntegerField() 

83 

84 # GenericForeignKey combines content_type and object_id 

85 # This allows the review to be associated with any product type 

86 # The 'product' name makes it easy to access the related product 

87 product = GenericForeignKey( 

88 'content_type', 

89 'object_id', 

90 ) 

91 

92 user = models.ForeignKey( 

93 to=User, 

94 on_delete=models.CASCADE, 

95 ) 

96 

97 def __str__(self): 

98 return f'{self.product} - {self.user.username} ({self.rating})'