File size: 2,730 Bytes
877587b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from django.contrib.auth.models import AbstractUser
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.db.models import Avg


class User(AbstractUser):
    """
    Custom user model with phone-based authentication.
    Phone number is unique and serves as the primary identifier.
    """
    phone = models.CharField(max_length=15, unique=True, db_index=True)
    name = models.CharField(max_length=255)
    
    # Username is not used but required by AbstractUser
    # We'll make it optional by setting blank=True
    username = models.CharField(max_length=150, blank=True, null=True)
    
    USERNAME_FIELD = 'phone'
    REQUIRED_FIELDS = ['name']
    
    class Meta:
        db_table = 'users'
        verbose_name = 'User'
        verbose_name_plural = 'Users'
    
    def __str__(self):
        return f"{self.name} ({self.phone})"


class Place(models.Model):
    """
    Represents a place that can be reviewed.
    Unique constraint on (name, address) ensures no duplicates.
    """
    name = models.CharField(max_length=255, db_index=True)
    address = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'places'
        unique_together = [['name', 'address']]
        verbose_name = 'Place'
        verbose_name_plural = 'Places'
        indexes = [
            models.Index(fields=['name']),
        ]
    
    def __str__(self):
        return f"{self.name} - {self.address[:50]}"
    
    @property
    def average_rating(self):
        """Calculate average rating from all reviews."""
        avg = self.reviews.aggregate(avg_rating=Avg('rating'))['avg_rating']
        return round(avg, 2) if avg is not None else None


class Review(models.Model):
    """
    Review left by a user for a place.
    Rating must be between 1-5.
    One review per user per place.
    """
    user = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        related_name='reviews'
    )
    place = models.ForeignKey(
        Place, 
        on_delete=models.CASCADE, 
        related_name='reviews'
    )
    rating = models.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(5)]
    )
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'reviews'
        unique_together = [['user', 'place']]
        verbose_name = 'Review'
        verbose_name_plural = 'Reviews'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['-created_at']),
        ]
    
    def __str__(self):
        return f"{self.user.name} - {self.place.name} ({self.rating}/5)"