from django.contrib.auth import get_user_model 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, ActualOnlyManager, AbstractDateTimeModel, ActiveOnlyManager # ---------------------------------- COMMON PRODUCT STATUS ---------------------------------------# # Create your models here. STATUS_INACTIVE = 0 STATUS_ACTIVE = 25 STATUS_DELETED = 50 STATUS_DEFAULT = STATUS_ACTIVE STATUS_CHOICES = ( (STATUS_INACTIVE, _('Неактивный')), (STATUS_ACTIVE, _('Активный')), (STATUS_DELETED, _('Удаленный')), ) # --------------------------------- PRODUCT ATTRIBUTE STATTUS------------------------------------# PRODUCT_ATTRIBUTE_TYPE_NONE = 0 PRODUCT_ATTRIBUTE_TYPE_RANGE = 50 PRODUCT_ATTRIBUTE_TYPE_SELECT = 100 PRODUCT_ATTRIBUTE_TYPE_CHOICES = ( (PRODUCT_ATTRIBUTE_TYPE_NONE, _('отсуствует')), (PRODUCT_ATTRIBUTE_TYPE_RANGE, _('диапазон')), (PRODUCT_ATTRIBUTE_TYPE_SELECT, _('выбор')) ) PRODUCT_ATTRIBUTE_TYPE_DEFAULT = PRODUCT_ATTRIBUTE_TYPE_NONE class ProductAttributeManager(ActiveOnlyManager): def get_range_type_attributes(self, product=None): pass def get_select_type_attributes(self, product=None): pass def get_all_type_attributes(self): pass class ProductAttribute(AbstractStatusModel): name = models.CharField(max_length=64, blank=True, null=True, default=None) slug = AutoSlugField(populate_from='name') type = models.SmallIntegerField(_('тип'), choices=PRODUCT_ATTRIBUTE_TYPE_CHOICES, default=PRODUCT_ATTRIBUTE_TYPE_DEFAULT) main_attribute = models.BooleanField(default=False) status = models.SmallIntegerField(_('статус'), default=STATUS_DEFAULT, choices=STATUS_CHOICES) objects = ProductAttributeManager() 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, null=True, verbose_name=("Изображение")) status = models.SmallIntegerField(_('статус'), default=STATUS_DEFAULT, choices=STATUS_CHOICES) 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, ActualOnlyManager): def get_categories(self, parent=None): return self.get_queryset().filter(parent=parent).all() class ProductActiveCategoryManager(mptt_models.TreeManager, ActiveOnlyManager): def get_categories(self, parent=None): return self.get_queryset().filter(parent=parent).all() class ProductCategory(MPTTModel, AbstractStatusModel): name = models.CharField(_('название'), db_index=True, unique=True, max_length=64, blank=True, null=True, default=None) slug = AutoSlugField(_('slug'), populate_from='name') parent = TreeForeignKey('self', verbose_name=_('родительская категория'), on_delete=models.CASCADE, null=True, blank=True, related_name='children') image = models.ImageField(_("иконка"), upload_to='categories', blank=True) status = models.SmallIntegerField(_('статус'), default=STATUS_DEFAULT, choices=STATUS_CHOICES) objects = ProductCategoryManager() active = ProductActiveCategoryManager() 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) slug = AutoSlugField(_('slug'), populate_from='name', db_index=True) description = models.TextField(_('описание'), blank=True, null=True, default=None) manufacturer = models.ForeignKey(Manufacturer, on_delete=models.SET_NULL, blank=True, null=True) category = models.ForeignKey(ProductCategory, on_delete=models.SET_NULL, blank=True, null=True) attributes = models.ManyToManyField(ProductAttribute, blank=True) status = models.SmallIntegerField(_('статус'), default=STATUS_DEFAULT, choices=STATUS_CHOICES) def __str__(self): return self.name def get_absolute_url(self, request): return request.build_absolute_uri(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 ProductRate(AbstractDateTimeModel): user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) product = models.ForeignKey(Product, on_delete=models.CASCADE) rate = models.IntegerField(_('оценка'), default=0) class Meta: verbose_name = _('Рейтинг продукта') verbose_name_plural = _('Рейтинг продукта') class ProductDiscount(AbstractStatusModel): product = models.ForeignKey(Product, on_delete=models.CASCADE) percentage = models.DecimalField(_('процент'), max_digits=3, decimal_places=2) status = models.SmallIntegerField(_('статус'), choices=STATUS_CHOICES, default=STATUS_DEFAULT) class Meta: verbose_name = _('Дисконт') verbose_name_plural = _('Дисконты') class ProductAttributeValue(AbstractStatusModel): attribute = models.ForeignKey(ProductAttribute, on_delete=models.CASCADE, related_name='value') value = HStoreField(_('значение'), default={}) def __str__(self): return self.value.serialize class Meta: unique_together = ('attribute', 'value') verbose_name = _('Значение аттрибута') verbose_name_plural = _('Значение аттрибутов') # ----------------- PRODUCT IMAGE STATUS LIST ------------------ 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(_('Статус'), choices=STATUS_CHOICES, default=STATUS_DEFAULT) 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 = _('Изображения продуктов')