from django.db import models from django.urls import reverse_lazy from django.contrib.postgres.fields import HStoreField from django.utils.translation import ugettext_lazy as _ from mptt import ( models as mptt_models, managers as mptt_managers, register ) from mptt.models import MPTTModel, TreeForeignKey from autoslug import AutoSlugField from core.models import AbstractStatusModel, AbstractDateTimeModel, STATUS_ACTIVE, STATUS_DELETED class ProductAttribute(AbstractStatusModel): name = models.CharField(max_length=64, blank=True, null=True, default=None) slug = AutoSlugField(populate_from='name') main_attribute = models.BooleanField(default=False) def __str__(self): return self.name class Meta: ordering = ('slug',) verbose_name = _('Аттрибут продукта') verbose_name_plural = _('Аттрибуты продукта') class Manufacturer(AbstractStatusModel): name = models.CharField(max_length=64, blank=True, null=True, default=None) slug = AutoSlugField(populate_from='name') image = models.ImageField(upload_to='producers', blank=True, verbose_name="image of producer") def __str__(self): return self.name def get_absolute_url(self): return reverse_lazy('products:manufacturer', kwargs={'producer_slug': self.slug, 'path': ''}) # @TODO: tranlsate into english and use traslation class Meta: verbose_name = _('Производитель') verbose_name_plural = _('Производители') class ProductCategoryManager(mptt_managers.TreeManager): def get_root_categories(self): return self.get_queryset().all() class ProductCategory(MPTTModel, AbstractDateTimeModel): name = models.CharField(db_index=True, unique=True, max_length=64, blank=True, null=True, default=None) slug = AutoSlugField(populate_from='name') parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') image = models.ImageField(upload_to='categories', blank=True, verbose_name="image of category") objects = ProductCategoryManager() def __str__(self): return self.name # @TODO: tranlsate into english and use traslation class Meta: verbose_name = _("Категория") verbose_name_plural = _("Категории") ordering = ('tree_id', 'level') class MPTTMeta: order_insertion_by = ['name'] register(ProductCategory, order_insertion_py=['name']) class Product(AbstractStatusModel): name = models.CharField(max_length=64, db_index=True, blank=True, null=True, default=None) slug = AutoSlugField(populate_from='name') price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) description = models.TextField(db_index=True, blank=True, null=True, default=None) manufacturer = models.ForeignKey(Manufacturer, on_delete=models.PROTECT) category = models.ForeignKey(ProductCategory, on_delete=models.SET_NULL, blank=True, null=True, default=None) attributes = models.ManyToManyField(ProductAttribute, blank=True) discount_policy = HStoreField(blank=True, null=True, default={}) is_active = models.BooleanField(default=True) def __str__(self): return self.name def get_absolute_url(self): return reverse_lazy('products:item', args=[self.slug]) class Meta: indexes = [ models.Index(fields=['id', 'slug']) ] verbose_name = _('Продукт') verbose_name_plural = _('Продукты') # def save(self, *args, **kwargs): # if self.category: # super(Product, self).save(*args, **kwargs) # # for cp in ProductClass.objects.filter(category=self.product_class): # pp = ProductProperty.objects.filter(category_property=cp, # products=self) # if not pp: # pp = ProductProperty(category_property=cp, products=self, value="--") # pp.save() class ProductAttributeValue(AbstractDateTimeModel): product = models.ForeignKey(Product, on_delete=models.PROTECT) name = models.CharField(max_length=64, blank=True, null=True, default=None) slug = AutoSlugField(populate_from='name') attribute = models.ForeignKey(ProductAttribute, on_delete=models.CASCADE, related_name='values') def __str__(self): return self.name class Meta: unique_together = ('name', 'attribute') verbose_name = 'Product attribute value' verbose_name_plural = 'Product attribute values' # ----------------- PRODUCT IMAGE STATUS LIST ------------------ PRODUCT_IMAGE_STATUS_CHOICES = ( (STATUS_ACTIVE, _('Активный')), (STATUS_DELETED, _('Удален')), ) PRODUCT_IMAGE_DEFAULT_STATUS = STATUS_ACTIVE class ProductImage(AbstractStatusModel): def get_file_path(self, filename): return "products/attachments/{product}/{filename}".format(**{ 'product': self.product.id, 'filename': filename }) product = models.ForeignKey(Product, on_delete=models.CASCADE) status = models.SmallIntegerField(_('Статус'), default=PRODUCT_IMAGE_DEFAULT_STATUS, choices=PRODUCT_IMAGE_STATUS_CHOICES) filename = models.CharField(_('Имя файла'), max_length=255) image = models.FileField(_('Изображение'), upload_to=get_file_path, max_length=500) is_default = models.BooleanField(_('По умолчанию'), default=False) @classmethod def create(cls, request, file): product_image = cls(request=request, file=file, filename=file.name) return product_image class Meta: verbose_name = _('Изображение продукта') verbose_name_plural = _('Изображения продуктов') # class Offer(models.Model): # name = models.CharField(max_length=64, blank=True, null=True, default=None) # slug = AutoSlugField(populate_from='name') # price = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00) # # points = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00) # products = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, default=None, # related_name='variants') # is_active = models.BooleanField(default=True) # attributes = HStoreField(blank=True, null=True, default={}) # # def __str__(self): # return self.name # # class Meta: # verbose_name = 'Offer' # verbose_name_plural = 'Offers' # # def save(self, *args, **kwargs): # self.points = self.price * decimal.Decimal('0.1') # super(Offer, self).save(*args, **kwargs)