gault-millau/apps/product/models.py
2019-10-28 13:11:07 +03:00

183 lines
6.6 KiB
Python

"""Product app models."""
from django.db import models
from django.core.exceptions import ValidationError
from django.contrib.postgres.fields import JSONField
from django.utils.translation import gettext_lazy as _
from utils.models import (BaseAttributes, ProjectBaseMixin,
TranslatedFieldsMixin, TJSONField)
class ProductType(TranslatedFieldsMixin, ProjectBaseMixin):
"""ProductType model."""
STR_FIELD_NAME = 'name'
# INDEX NAME CHOICES
FOOD = 'food'
WINE = 'wine'
LIQUOR = 'liquor'
INDEX_NAME_TYPES = (
(FOOD, _('Food')),
(WINE, _('Wine')),
(LIQUOR, _('Liquor')),
)
name = TJSONField(blank=True, null=True, default=None,
verbose_name=_('Name'), help_text='{"en-GB":"some text"}')
index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
unique=True, db_index=True,
verbose_name=_('Index name'))
use_subtypes = models.BooleanField(_('Use subtypes'), default=True)
class Meta:
"""Meta class."""
verbose_name = _('Product type')
verbose_name_plural = _('Product types')
class ProductSubType(TranslatedFieldsMixin, ProjectBaseMixin):
"""ProductSubtype model."""
STR_FIELD_NAME = 'name'
# INDEX NAME CHOICES
RUM = 'rum'
OTHER = 'other'
INDEX_NAME_TYPES = (
(RUM, _('Rum')),
(OTHER, _('Other')),
)
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE,
related_name='subtypes',
verbose_name=_('Product type'))
name = TJSONField(blank=True, null=True, default=None,
verbose_name=_('Name'), help_text='{"en-GB":"some text"}')
index_name = models.CharField(max_length=50, choices=INDEX_NAME_TYPES,
unique=True, db_index=True,
verbose_name=_('Index name'))
class Meta:
"""Meta class."""
verbose_name = _('Product subtype')
verbose_name_plural = _('Product subtypes')
def clean_fields(self, exclude=None):
if not self.product_type.use_subtypes:
raise ValidationError(_('Product type is not use subtypes.'))
class ProductManager(models.Manager):
"""Extended manager for Product model."""
class ProductQuerySet(models.QuerySet):
"""Product queryset."""
def with_base_related(self):
return self.select_related('product_type', 'establishment') \
.prefetch_related('product_type__subtypes', 'country')
def common(self):
return self.filter(category=self.model.COMMON)
def online(self):
return self.filter(category=self.model.ONLINE)
def wines(self):
return self.filter(type__index_name=ProductType.WINE)
def by_product_type(self, product_type: str):
"""Filter by type."""
return self.filter(product_type__index_name=product_type)
def by_product_subtype(self, product_subtype: str):
"""Filter by subtype."""
return self.filter(subtypes__index_name=product_subtype)
class Product(TranslatedFieldsMixin, BaseAttributes):
"""Product models."""
STR_FIELD_NAME = 'name'
COMMON = 0
ONLINE = 1
CATEGORY_CHOICES = (
(COMMON, _('Common')),
(ONLINE, _('Online')),
)
category = models.PositiveIntegerField(choices=CATEGORY_CHOICES,
default=COMMON)
name = TJSONField(_('Name'), null=True, blank=True, default=None,
help_text='{"en-GB":"some text"}')
description = TJSONField(_('Description'), null=True, blank=True,
default=None, help_text='{"en-GB":"some text"}')
characteristics = JSONField(_('Characteristics'))
country = models.ManyToManyField('location.Country',
verbose_name=_('Country'))
available = models.BooleanField(_('Available'), default=True)
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT,
related_name='products', verbose_name=_('Type'))
subtypes = models.ManyToManyField(ProductSubType, blank=True,
related_name='products',
verbose_name=_('Subtypes'))
establishment = models.ForeignKey('establishment.Establishment',
on_delete=models.PROTECT,
related_name='products',
verbose_name=_('establishment'))
public_mark = models.PositiveIntegerField(blank=True, null=True, default=None,
verbose_name=_('public mark'),)
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.PROTECT,
related_name='wines',
blank=True, null=True,
verbose_name=_('wine region'))
wine_appellation = models.ForeignKey('location.WineAppellation', on_delete=models.PROTECT,
blank=True, null=True,
verbose_name=_('wine appellation'))
objects = ProductManager.from_queryset(ProductQuerySet)()
class Meta:
"""Meta class."""
verbose_name = _('Product')
verbose_name_plural = _('Products')
def clean_fields(self, exclude=None):
super().clean_fields(exclude=exclude)
if self.product_type.index_name == ProductType.WINE and not self.wine_region:
raise ValidationError(_('wine_region field must be specified.'))
if not self.product_type.index_name == ProductType.WINE and self.wine_region:
raise ValidationError(_('wine_region field must not be specified.'))
if (self.wine_region and self.wine_appellation) and \
self.wine_appellation not in self.wine_region.appellations.all():
raise ValidationError(_('Wine appellation not exists in wine region.'))
class OnlineProductManager(ProductManager):
"""Extended manger for OnlineProduct model."""
def get_queryset(self):
"""Overridden get_queryset method."""
return super().get_queryset().online()
class OnlineProduct(Product):
"""Online product."""
objects = OnlineProductManager.from_queryset(ProductQuerySet)()
class Meta:
"""Meta class."""
proxy = True
verbose_name = _('Online product')
verbose_name_plural = _('Online products')