from django.db import models from django.db.models import functions from django.core.validators import MinValueValidator, MaxValueValidator from apps.core.history import TrackedModel import pghistory @pghistory.track() class ParkReview(TrackedModel): # Import managers from ..managers import ParkReviewManager objects = ParkReviewManager() """ A review of a park. """ park = models.ForeignKey( "parks.Park", on_delete=models.CASCADE, related_name="reviews" ) user = models.ForeignKey( "accounts.User", on_delete=models.CASCADE, related_name="park_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_park_reviews", ) moderated_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ["-created_at"] unique_together = ["park", "user"] constraints = [ # Business rule: Rating must be between 1 and 10 (database level # enforcement) models.CheckConstraint( name="park_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="park_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="park_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.park.name} by {self.user.username}"