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
« 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
7from src.products.constants import ReviewFieldLengths
9User = get_user_model()
12class Review(models.Model):
13 """
14 Review model for user feedback on products.
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.
21 The model includes an approval system where reviews must be approved
22 before being displayed publicly, allowing for content moderation.
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 """
32 RATING_CHOICES = [(i, str(i)) for i in range(1, 6)]
34 class Meta:
35 ordering = ['-created_at']
37 # Ensure each user can only review each product once
38 unique_together = (
39 'user',
40 'content_type',
41 'object_id',
42 )
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 ]
53 rating = models.IntegerField(
54 choices=RATING_CHOICES,
55 )
57 comment = models.TextField(
58 validators=[
59 MaxLengthValidator(
60 ReviewFieldLengths.MAX_COMMENT_LENGTH,
61 ),
62 ],
63 )
65 # Only approved reviews are displayed publicly
66 approved = models.BooleanField(
67 default=False,
68 )
70 created_at = models.DateTimeField(
71 auto_now_add=True,
72 )
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 )
81 # object_id stores the specific product's ID
82 object_id = models.PositiveIntegerField()
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 )
92 user = models.ForeignKey(
93 to=User,
94 on_delete=models.CASCADE,
95 )
97 def __str__(self):
98 return f'{self.product} - {self.user.username} ({self.rating})'