from django.db import models from django.core.exceptions import ValidationError class Category(models.Model): name = models.CharField(max_length=255, verbose_name='Название') slug = models.SlugField(unique=True, verbose_name='URL') class Meta: verbose_name = 'Категория' verbose_name_plural = 'Категории' def __str__(self): return self.name class Subcategory(models.Model): category = models.ForeignKey( Category, on_delete=models.CASCADE, related_name='subcategories', verbose_name='Категория', ) name = models.CharField(max_length=255, verbose_name='Название') slug = models.SlugField(verbose_name='URL') class Meta: ordering = ('category__name', 'name') constraints = [ models.UniqueConstraint( fields=('category', 'slug'), name='unique_subcategory_slug_per_category', ) ] verbose_name = 'Подкатегория' verbose_name_plural = 'Подкатегории' def __str__(self): return self.name class Item(models.Model): name = models.CharField(max_length=255, verbose_name='Название') slug = models.SlugField(unique=True, verbose_name='URL') category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='items', verbose_name='Категория') subcategory = models.ForeignKey( Subcategory, on_delete=models.SET_NULL, related_name='items', null=True, blank=True, verbose_name='Подкатегория', ) description = models.TextField(blank=True, verbose_name='Описание') price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Цена') is_available = models.BooleanField(default=True, verbose_name='В наличии') created_at = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания') updated_at = models.DateTimeField(auto_now=True, verbose_name='Дата обновления') class Meta: verbose_name = 'Товар' verbose_name_plural = 'Товары' def __str__(self): return self.name def clean(self): super().clean() if self.subcategory_id and self.category_id and self.subcategory.category_id != self.category_id: raise ValidationError({ 'subcategory': 'Подкатегория должна относиться к выбранной категории.' }) @property def primary_image(self): return next(iter(self.images.all()), None) class ItemImage(models.Model): item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='images', verbose_name='Товар') image = models.ImageField(upload_to='items/', verbose_name='Изображение') alt = models.CharField(max_length=255, blank=True, verbose_name='Alt-текст') sort_order = models.PositiveIntegerField(default=0, verbose_name='Порядок') is_primary = models.BooleanField(default=False, verbose_name='Главное изображение') class Meta: ordering = ('-is_primary', 'sort_order', 'id') verbose_name = 'Изображение товара' verbose_name_plural = 'Изображения товара' def __str__(self): return self.alt or f'Изображение товара {self.item.name}'