Python · 3172 bytes Raw Blame History
1 from django.db import models
2 from django.core.validators import MinValueValidator, MaxValueValidator
3 from django.utils import timezone
4
5
6 class Restaurant(models.Model):
7 """A restaurant that might serve toast"""
8 place_id = models.CharField(max_length=255, unique=True, db_index=True)
9 name = models.CharField(max_length=255)
10 address = models.TextField()
11 latitude = models.FloatField()
12 longitude = models.FloatField()
13 created_at = models.DateTimeField(default=timezone.now)
14
15 # Toast availability status
16 has_toast = models.BooleanField(null=True, blank=True, help_text="Does this place serve toast?")
17
18 # Cached rating fields (updated via signals)
19 average_rating = models.FloatField(null=True, blank=True)
20 total_ratings = models.IntegerField(default=0)
21
22 class Meta:
23 ordering = ['-created_at']
24 indexes = [
25 models.Index(fields=['latitude', 'longitude']),
26 ]
27
28 def __str__(self):
29 return self.name
30
31 def update_rating_cache(self):
32 """Update the cached rating values"""
33 ratings = self.ratings.all()
34 if ratings.exists():
35 self.total_ratings = ratings.count()
36 self.average_rating = ratings.aggregate(
37 avg_rating=models.Avg('rating')
38 )['avg_rating']
39 else:
40 self.total_ratings = 0
41 self.average_rating = None
42 self.save(update_fields=['average_rating', 'total_ratings'])
43
44
45 class Rating(models.Model):
46 """A toast rating for a restaurant"""
47 restaurant = models.ForeignKey(
48 Restaurant,
49 on_delete=models.CASCADE,
50 related_name='ratings'
51 )
52 rating = models.IntegerField(
53 validators=[MinValueValidator(1), MaxValueValidator(5)]
54 )
55 review = models.TextField()
56 created_at = models.DateTimeField(default=timezone.now)
57
58 class Meta:
59 ordering = ['-created_at']
60
61 def __str__(self):
62 return f"{self.restaurant.name} - {self.rating} stars"
63
64 def clean(self):
65 """Validate that the review mentions toast"""
66 from django.core.exceptions import ValidationError
67
68 if self.review:
69 toast_keywords = [
70 'toast', 'bread', 'butter', 'jam', 'marmalade',
71 'french toast', 'avocado', 'sourdough', 'rye',
72 'whole wheat', 'brioche', 'challah'
73 ]
74 review_lower = self.review.lower()
75 if not any(keyword in review_lower for keyword in toast_keywords):
76 raise ValidationError(
77 'Reviews must be about toast! Please mention the toast in your review.'
78 )
79 super().clean()
80
81 def save(self, *args, **kwargs):
82 self.full_clean() # Runs clean() method
83 super().save(*args, **kwargs)
84 # Update restaurant's cached ratings
85 self.restaurant.update_rating_cache()
86
87 def delete(self, *args, **kwargs):
88 restaurant = self.restaurant
89 super().delete(*args, **kwargs)
90 # Update restaurant's cached ratings after deletion
91 restaurant.update_rating_cache()