|
|
|
|
@ -3,11 +3,15 @@ from django.urls import reverse_lazy |
|
|
|
|
from django.contrib.postgres.fields import HStoreField |
|
|
|
|
from django.utils.translation import ugettext_lazy as _ |
|
|
|
|
|
|
|
|
|
import mptt |
|
|
|
|
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 |
|
|
|
|
from core.models import AbstractStatusModel, AbstractDateTimeModel, STATUS_ACTIVE, STATUS_DELETED |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProductAttribute(AbstractStatusModel): |
|
|
|
|
@ -20,8 +24,9 @@ class ProductAttribute(AbstractStatusModel): |
|
|
|
|
|
|
|
|
|
class Meta: |
|
|
|
|
ordering = ('slug',) |
|
|
|
|
verbose_name = _('Product attribute') |
|
|
|
|
verbose_name_plural = _('Product attributes') |
|
|
|
|
verbose_name = _('Аттрибут продукта') |
|
|
|
|
verbose_name_plural = _('Аттрибуты продукта') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Manufacturer(AbstractStatusModel): |
|
|
|
|
name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
|
|
|
@ -34,31 +39,39 @@ class Manufacturer(AbstractStatusModel): |
|
|
|
|
def get_absolute_url(self): |
|
|
|
|
return reverse_lazy('products:manufacturer', kwargs={'producer_slug': self.slug, 'path': ''}) |
|
|
|
|
|
|
|
|
|
#@TODO: tranlsate into english and use traslation |
|
|
|
|
# @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 = 'Product\'s category' |
|
|
|
|
verbose_name_plural = 'Product\'s categories' |
|
|
|
|
verbose_name = _("Категория") |
|
|
|
|
verbose_name_plural = _("Категории") |
|
|
|
|
ordering = ('tree_id', 'level') |
|
|
|
|
|
|
|
|
|
class MPTTMeta: |
|
|
|
|
order_insertion_by = ['name'] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mptt.register(ProductCategory, order_insertion_py=['name']) |
|
|
|
|
register(ProductCategory, order_insertion_py=['name']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Product(AbstractStatusModel): |
|
|
|
|
@ -67,7 +80,6 @@ class Product(AbstractStatusModel): |
|
|
|
|
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) |
|
|
|
|
image = models.ImageField(upload_to='products', blank=True, verbose_name="image of products") |
|
|
|
|
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={}) |
|
|
|
|
@ -83,8 +95,8 @@ class Product(AbstractStatusModel): |
|
|
|
|
indexes = [ |
|
|
|
|
models.Index(fields=['id', 'slug']) |
|
|
|
|
] |
|
|
|
|
verbose_name = _('Product') |
|
|
|
|
verbose_name_plural = _('Products') |
|
|
|
|
verbose_name = _('Продукт') |
|
|
|
|
verbose_name_plural = _('Продукты') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -114,6 +126,40 @@ class ProductAttributeValue(AbstractDateTimeModel): |
|
|
|
|
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') |
|
|
|
|
|