from django.db import models from django.db.models import functions from django.core.validators import MinValueValidator, MaxValueValidator from core.history import TrackedModel import pghistory @pghistory.track() class RideReview(TrackedModel): """ A review of a ride. """ ride = models.ForeignKey( 'rides.Ride', on_delete=models.CASCADE, related_name='reviews' ) user = models.ForeignKey( 'accounts.User', on_delete=models.CASCADE, related_name='ride_reviews' ) rating = models.PositiveSmallIntegerField( validators=[MinValueValidator(1), MaxValueValidator(10)] ) title = models.CharField(max_length=200) content = models.TextField() visit_date = models.DateField() # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) # Moderation is_published = models.BooleanField(default=True) moderation_notes = models.TextField(blank=True) moderated_by = models.ForeignKey( 'accounts.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='moderated_ride_reviews' ) moderated_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ['-created_at'] unique_together = ['ride', 'user'] constraints = [ # Business rule: Rating must be between 1 and 10 (database level enforcement) models.CheckConstraint( name="ride_review_rating_range", check=models.Q(rating__gte=1) & models.Q(rating__lte=10), violation_error_message="Rating must be between 1 and 10" ), # Business rule: Visit date cannot be in the future models.CheckConstraint( name="ride_review_visit_date_not_future", check=models.Q(visit_date__lte=functions.Now()), violation_error_message="Visit date cannot be in the future" ), # Business rule: If moderated, must have moderator and timestamp models.CheckConstraint( name="ride_review_moderation_consistency", check=models.Q(moderated_by__isnull=True, moderated_at__isnull=True) | models.Q(moderated_by__isnull=False, moderated_at__isnull=False), violation_error_message="Moderated reviews must have both moderator and moderation timestamp" ), ] def __str__(self): return f"Review of {self.ride.name} by {self.user.username}"