Files
holy-flavor-rater/backend/api/models.py
T
2026-02-16 19:16:47 +01:00

114 lines
4.2 KiB
Python

from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.validators import MinValueValidator, MaxValueValidator
class User(AbstractUser):
THEME_CHOICES = [
('latte', 'Latte'),
('pink', 'Pastel Pink'),
('solarized_light', 'Solarized Light'),
('one_light', 'One Light'),
('paper', 'Paper White'),
('frappe', 'Frappé'),
('macchiato', 'Macchiato'),
('mocha', 'Mocha'),
('atom', 'Atom One Dark'),
('dracula', 'Dracula'),
('gruvbox', 'Gruvbox Dark'),
('nord', 'Nord'),
('cyberpunk', 'Cyberpunk'),
('forest', 'Forest Dark'),
]
theme = models.CharField(max_length=20, choices=THEME_CHOICES, default='mocha')
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
pending_email = models.EmailField(max_length=254, blank=True, null=True)
email_confirmation_code = models.CharField(max_length=6, blank=True, null=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.avatar:
try:
from PIL import Image
img = Image.open(self.avatar.path)
if img.height > 256 or img.width > 256 or img.format != 'JPEG':
output_size = (256, 256)
img.thumbnail(output_size)
if img.mode in ("RGBA", "P"):
img = img.convert("RGB")
img.save(self.avatar.path, 'JPEG', quality=90)
except Exception:
pass
def __str__(self):
return self.username
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Flavor(models.Model):
category = models.ForeignKey(Category, related_name='flavors', on_delete=models.CASCADE)
name = models.CharField(max_length=100) # Removed unique=True here
description = models.TextField(blank=True)
image_url = models.URLField(max_length=1000, blank=True, null=True)
image = models.ImageField(upload_to='flavors/', blank=True, null=True)
shop_url = models.URLField(max_length=500, blank=True, null=True)
is_available = models.BooleanField(default=True)
is_legacy = models.BooleanField(default=False)
external_id = models.BigIntegerField(unique=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# This allows "Erdbeere" in Iced Tea and "Erdbeere" in Milkshake
constraints = [
models.UniqueConstraint(fields=['name', 'category'], name='unique_flavor_name_per_category')
]
def __str__(self):
return f"{self.name} ({self.category.name})"
@property
def get_average_rating(self):
ratings = self.ratings.all()
if ratings.exists():
return sum(r.score for r in ratings) / ratings.count()
return 0.0
@property
def cached_image_url(self):
if self.image:
return self.image.url
return self.image_url
class Rating(models.Model):
user = models.ForeignKey(User, related_name='ratings', on_delete=models.CASCADE)
flavor = models.ForeignKey(Flavor, related_name='ratings', on_delete=models.CASCADE)
score = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(10)])
comment = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('user', 'flavor')
ordering = ['-created_at']
def __str__(self):
return f"{self.user.username} - {self.flavor.name}: {self.score}"
class Reply(models.Model):
user = models.ForeignKey(User, related_name='replies', on_delete=models.CASCADE)
rating = models.ForeignKey(Rating, related_name='replies', on_delete=models.CASCADE)
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at']
def __str__(self):
return f"Reply by {self.user.username} on {self.rating.id}"