Assignment / reviews /models.py
Assignment User
Initial commit: Django REST API for place reviews
877587b
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)"