added guide type field, added tasks, refactored GuideElement model, added endpoint,
This commit is contained in:
parent
65746b1cd0
commit
d9df886b98
14
apps/collection/migrations/0029_merge_20191225_1819.py
Normal file
14
apps/collection/migrations/0029_merge_20191225_1819.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-25 18:19
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collection', '0028_merge_20191223_1415'),
|
||||
('collection', '0028_guide_initial_objects_counter'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
18
apps/collection/migrations/0030_guidefilter_guide_type.py
Normal file
18
apps/collection/migrations/0030_guidefilter_guide_type.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-25 18:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collection', '0029_merge_20191225_1819'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='guidefilter',
|
||||
name='guide_type',
|
||||
field=models.PositiveSmallIntegerField(choices=[(0, 'Restaurant'), (1, 'Artisan'), (2, 'Wine')], default=0, verbose_name='guide type'),
|
||||
),
|
||||
]
|
||||
|
|
@ -5,17 +5,19 @@ from django.contrib.postgres.fields import JSONField
|
|||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from mptt.models import MPTTModel
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
from slugify import slugify
|
||||
|
||||
from location.models import Country, Region, WineRegion, WineSubRegion
|
||||
from location.models import Country, Region, WineRegion, WineSubRegion, City
|
||||
from review.models import Review
|
||||
from product.models import Product
|
||||
from translation.models import Language
|
||||
from utils.models import IntermediateGalleryModelMixin
|
||||
from utils.models import (
|
||||
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
|
||||
URLImageMixin, IntermediateGalleryModelMixin
|
||||
)
|
||||
from utils.methods import slug_into_section_name
|
||||
from utils.querysets import RelatedObjectsCountMixin
|
||||
|
||||
|
||||
|
|
@ -381,6 +383,20 @@ class GuideFilterQuerySet(models.QuerySet):
|
|||
|
||||
class GuideFilter(ProjectBaseMixin):
|
||||
"""Guide filter model."""
|
||||
|
||||
RESTAURANT = 0
|
||||
ARTISAN = 1
|
||||
WINE = 2
|
||||
|
||||
GUIDE_TYPES = (
|
||||
(RESTAURANT, _('Restaurant')),
|
||||
(ARTISAN, _('Artisan')),
|
||||
(WINE, _('Wine')),
|
||||
)
|
||||
|
||||
guide_type = models.PositiveSmallIntegerField(choices=GUIDE_TYPES,
|
||||
default=RESTAURANT,
|
||||
verbose_name=_('guide type'))
|
||||
establishment_type_json = JSONField(blank=True, null=True,
|
||||
verbose_name='establishment types')
|
||||
country_json = JSONField(blank=True, null=True,
|
||||
|
|
@ -568,13 +584,73 @@ class GuideFilter(ProjectBaseMixin):
|
|||
'public_mark__isnull': False,
|
||||
})
|
||||
|
||||
if self.locale_json:
|
||||
if self.locales:
|
||||
filters.update({
|
||||
'reviews__text__has_any_keys': self.locales,
|
||||
})
|
||||
|
||||
return filters
|
||||
|
||||
@property
|
||||
def product_filter_set(self):
|
||||
filters = {
|
||||
# establishment.Establishment
|
||||
'establishment__public_mark__in': [self.min_mark, self.max_mark],
|
||||
# review.Reviews
|
||||
'reviews__vintage__in': self.review_vintages,
|
||||
}
|
||||
|
||||
if self.establishment_type_ids:
|
||||
filters.update({
|
||||
# establishment.EstablishmentType
|
||||
'establishment__establishment_type_id__in': self.establishment_type_ids,
|
||||
})
|
||||
|
||||
if self.country_ids:
|
||||
filters.update({
|
||||
# location.Country
|
||||
'establishment__address__city__country_id__in': self.country_ids,
|
||||
})
|
||||
|
||||
if self.region_ids:
|
||||
filters.update({
|
||||
# location.Region
|
||||
'establishment__address__city__region__parent_id__in': self.region_ids,
|
||||
})
|
||||
|
||||
if self.sub_region_ids:
|
||||
filters.update({
|
||||
# location.Region
|
||||
'establishment__address__city__region__parent_id__in': self.region_ids,
|
||||
'establishment__address__city__region_id__in': self.sub_region_ids,
|
||||
})
|
||||
|
||||
if self.wine_region_ids:
|
||||
filters.update({
|
||||
# location.WineRegion
|
||||
'wine_origins__wine_region_id__in': self.wine_region_ids,
|
||||
})
|
||||
|
||||
if self.with_mark:
|
||||
filters.update({
|
||||
# establishment.Establishment
|
||||
'establishment__public_mark__isnull': False,
|
||||
})
|
||||
|
||||
if self.locales:
|
||||
filters.update({
|
||||
'reviews__text__has_any_keys': self.locales,
|
||||
})
|
||||
|
||||
return filters
|
||||
|
||||
@property
|
||||
def filter_set(self):
|
||||
if self.guide_type in [self.RESTAURANT, self.ARTISAN]:
|
||||
return self.establishment_filter_set
|
||||
elif self.guide_type == self.WINE:
|
||||
return self.product_filter_set
|
||||
|
||||
|
||||
class GuideElementType(models.Model):
|
||||
"""Model for type of guide elements."""
|
||||
|
|
@ -657,6 +733,138 @@ class GuideElementManager(models.Manager):
|
|||
if qs.exists():
|
||||
return qs.first()
|
||||
|
||||
def get_or_create_root_node(self, guide_id: int):
|
||||
"""Get or Create RootNode."""
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='Root')
|
||||
guide_qs = Guide.objects.filter(id=guide_id)
|
||||
|
||||
if guide_element_type_qs.exists() and guide_qs.exists():
|
||||
guide = guide_qs.first()
|
||||
return self.get_or_create(guide_id=guide_qs.first(),
|
||||
guide_element_type=guide_element_type_qs.first(),
|
||||
defaults={
|
||||
'guide_id': guide.id,
|
||||
'guide_element_type': guide_element_type_qs.first()})
|
||||
return None, False
|
||||
|
||||
def get_or_create_city_node(self, root_node_id: int, city_id: int):
|
||||
"""Get or Create CityNode."""
|
||||
parent_node_qs = GuideElement.objects.filter(id=root_node_id)
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='CityNode')
|
||||
city_qs = City.objects.filter(id=city_id)
|
||||
|
||||
if parent_node_qs.exists() and city_qs.exists() and guide_element_type_qs.exists():
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=parent_node_qs.first(),
|
||||
city=city_qs.first())
|
||||
return None, False
|
||||
|
||||
def get_or_create_establishment_section_node(self, city_node: int, establishment_node_name: str):
|
||||
"""Get or Create (Restaurant|Shop...)SectionNode."""
|
||||
parent_node_qs = GuideElement.objects.filter(id=city_node)
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name__iexact=establishment_node_name)
|
||||
|
||||
if parent_node_qs.exists() and guide_element_type_qs.exists():
|
||||
parent_node = parent_node_qs.first()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=parent_node,
|
||||
guide=parent_node.get_root().guide)
|
||||
return None, False
|
||||
|
||||
def get_or_create_establishment_node(self, restaurant_section_node_id: int,
|
||||
establishment_id: int, review_id: int = None):
|
||||
"""Get or Create EstablishmentNode."""
|
||||
from establishment.models import Establishment
|
||||
|
||||
data = {}
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='EstablishmentNode')
|
||||
parent_node_qs = GuideElement.objects.filter(id=restaurant_section_node_id)
|
||||
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
||||
|
||||
if parent_node_qs.exists() and establishment_qs.exists() and guide_element_type_qs.exists():
|
||||
establishment = establishment_qs.first()
|
||||
parent_node = parent_node_qs.first()
|
||||
data.update({
|
||||
'guide_element_type': guide_element_type_qs.first(),
|
||||
'parent': parent_node,
|
||||
'guide': parent_node.get_root().guide,
|
||||
'establishment': establishment
|
||||
})
|
||||
if review_id:
|
||||
review_qs = Review.objects.filter(id=review_id)
|
||||
if review_qs.exists(): data.update({'review_id': review_qs.first().id})
|
||||
return self.get_or_create(**data)
|
||||
return None, False
|
||||
|
||||
def get_or_create_wine_region_node(self, root_node_id: int, wine_region_id: int):
|
||||
"""Get or Create WineRegionNode."""
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='RegionNode')
|
||||
parent_node_qs = GuideElement.objects.filter(id=root_node_id)
|
||||
wine_region_qs = WineRegion.objects.filter(id=wine_region_id)
|
||||
|
||||
if parent_node_qs.exists() and parent_node_qs.first().guide and wine_region_qs.exists() and guide_element_type_qs.exists():
|
||||
root_node = parent_node_qs.first()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=root_node,
|
||||
guide=root_node.guide,
|
||||
wine_region=wine_region_qs.first())
|
||||
return None, False
|
||||
|
||||
def get_or_create_yard_node(self, product_id: int, wine_region_node_id: int):
|
||||
"""Make YardNode."""
|
||||
from establishment.models import Establishment
|
||||
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='YardNode')
|
||||
wine_region_node_qs = GuideElement.objects.filter(id=wine_region_node_id)
|
||||
product_qs = Product.objects.filter(id=product_id)
|
||||
|
||||
if product_qs.exists() and wine_region_node_qs.exists():
|
||||
wine_region_node = wine_region_node_qs.first()
|
||||
root_node = wine_region_node.get_root()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=wine_region_node,
|
||||
guide=root_node.guide,
|
||||
product=product_qs.first())
|
||||
return None, False
|
||||
|
||||
def get_or_create_color_wine_section_node(self, wine_color_name: str, yard_node_id: int):
|
||||
"""Get or Create WineSectionNode."""
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='ColorWineSectionNode')
|
||||
parent_node_qs = GuideElement.objects.filter(id=yard_node_id)
|
||||
|
||||
if not wine_color_name.endswith('SectionNode'):
|
||||
wine_color_name = slug_into_section_name(wine_color_name)
|
||||
|
||||
wine_color_section, _ = GuideWineColorSection.objects.get_or_create(
|
||||
name=wine_color_name,
|
||||
defaults={
|
||||
'name': wine_color_name
|
||||
})
|
||||
|
||||
if parent_node_qs.exists() and guide_element_type_qs.exists():
|
||||
root_node = parent_node_qs.first().get_root()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=root_node,
|
||||
wine_color_section=wine_color_section,
|
||||
guide=root_node.guide)
|
||||
return None, False
|
||||
|
||||
def get_or_create_wine_node(self, color_wine_section_node_id: int, wine_id: int, review_id: int):
|
||||
"""Get or Create WineNode."""
|
||||
guide_element_type_qs = GuideElementType.objects.filter(name='WineNode')
|
||||
parent_node_qs = GuideElement.objects.filter(id=color_wine_section_node_id)
|
||||
wine_qs = Product.objects.wines().filter(id=wine_id)
|
||||
review_qs = Review.objects.filter(id=review_id)
|
||||
|
||||
if parent_node_qs.exists() and wine_qs.exists() and review_qs.exists() and guide_element_type_qs.exists():
|
||||
root_node = parent_node_qs.first().get_root()
|
||||
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||
parent=root_node,
|
||||
product=wine_qs.first(),
|
||||
guide=root_node.guide,
|
||||
review=review_qs.first())
|
||||
return None, False
|
||||
|
||||
|
||||
class GuideElementQuerySet(models.QuerySet):
|
||||
"""QuerySet for model Guide elements."""
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from rest_framework_recursive.fields import RecursiveField
|
|||
from establishment.serializers import EstablishmentGuideElementSerializer
|
||||
from product.serializers import ProductGuideElementSerializer
|
||||
from django.shortcuts import get_object_or_404
|
||||
from utils import exceptions
|
||||
|
||||
|
||||
class CollectionBaseSerializer(serializers.ModelSerializer):
|
||||
|
|
@ -203,3 +204,40 @@ class GuideElementBaseSerializer(serializers.ModelSerializer):
|
|||
'parent',
|
||||
'label_photo',
|
||||
]
|
||||
|
||||
|
||||
class AdvertorialBaseSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for model Advertorial."""
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
model = models.Advertorial
|
||||
fields = [
|
||||
'number_of_pages',
|
||||
'right_pages',
|
||||
'guide_element',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'guide_element': {'required': False}
|
||||
}
|
||||
|
||||
@property
|
||||
def request_kwargs(self):
|
||||
return self.context.get('request').parser_context.get('kwargs')
|
||||
|
||||
def validate(self, attrs):
|
||||
# check existence in guide
|
||||
guide = get_object_or_404(models.Guide.objects.all(),
|
||||
pk=self.request_kwargs.get('pk'))
|
||||
root_node = models.GuideElement.objects.get_root_node(guide)
|
||||
guide_element_qs = root_node.get_children().filter(pk=self.request_kwargs.get('element_pk'))
|
||||
guide_element = guide_element_qs.first()
|
||||
|
||||
if not guide_element_qs.exists():
|
||||
raise exceptions.GuideElementError()
|
||||
|
||||
if models.Advertorial.objects.filter(guide_element=guide_element).exists():
|
||||
raise exceptions.AdvertorialError()
|
||||
|
||||
attrs['guide_element'] = guide_element
|
||||
return attrs
|
||||
|
|
|
|||
|
|
@ -1,21 +1,133 @@
|
|||
"""Collectoin app celery tasks."""
|
||||
"""Collection app celery tasks."""
|
||||
import logging
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from collection import models as collection_models
|
||||
|
||||
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# todo: finish this
|
||||
def get_additional_establishment_data(section_node, establishment):
|
||||
data = [
|
||||
section_node.id,
|
||||
establishment.id,
|
||||
]
|
||||
if establishment.last_published_review:
|
||||
data.append(establishment.last_published_review.id)
|
||||
return data
|
||||
|
||||
|
||||
def get_additional_product_data(section_node, product):
|
||||
data = [
|
||||
section_node.id,
|
||||
product.id,
|
||||
]
|
||||
if product.last_published_review:
|
||||
data.append(product.last_published_review.id)
|
||||
return data
|
||||
|
||||
|
||||
@shared_task
|
||||
def generate_guide_elements(guide_id: int):
|
||||
"""Send verification email to user."""
|
||||
def generate_establishment_guide_elements(guide_id: int, queryset_values: dict, section_node_name: str):
|
||||
"""Generate guide elements."""
|
||||
from collection.models import GuideElement, Guide
|
||||
from establishment.models import Establishment
|
||||
|
||||
guide = Guide.objects.get(id=guide_id)
|
||||
try:
|
||||
obj = collection_models.Guide.objects.get(id=guide_id)
|
||||
for instance in queryset_values:
|
||||
establishment_id = instance.get('id')
|
||||
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
||||
if establishment_qs.exists():
|
||||
establishment = establishment_qs.first()
|
||||
root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id)
|
||||
if root_node:
|
||||
city_node, _ = GuideElement.objects.get_or_create_city_node(root_node.id,
|
||||
establishment.address.city_id)
|
||||
if city_node:
|
||||
section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
|
||||
city_node.id,
|
||||
section_node_name,
|
||||
)
|
||||
if section_node:
|
||||
GuideElement.objects.get_or_create_establishment_node(
|
||||
*get_additional_establishment_data(section_node=section_node,
|
||||
establishment=establishment))
|
||||
else:
|
||||
logger.error(
|
||||
f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - SectionNode is not exists.')
|
||||
else:
|
||||
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - CityNode is not exists.')
|
||||
else:
|
||||
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - RootNode is not exists.')
|
||||
else:
|
||||
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
|
||||
except Exception as e:
|
||||
logger.error(f'METHOD_NAME: {generate_guide_elements.__name__}\n'
|
||||
f'DETAIL: guide {guide_id}, - {e}')
|
||||
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||
else:
|
||||
guide.update_count_related_objects()
|
||||
# Update tree indexes
|
||||
GuideElement._tree_manager.rebuild()
|
||||
|
||||
|
||||
@shared_task
|
||||
def generate_product_guide_elements(guide_id: int, queryset_values: dict):
|
||||
"""Generate guide elements."""
|
||||
from collection.models import GuideElement, Guide
|
||||
from product.models import Product
|
||||
|
||||
guide = Guide.objects.get(id=guide_id)
|
||||
try:
|
||||
for instance in queryset_values:
|
||||
wine_id = instance.get('id')
|
||||
wine_qs = Product.objects.filter(id=wine_id)
|
||||
if wine_qs.exists():
|
||||
wine = wine_qs.first()
|
||||
root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id)
|
||||
if root_node:
|
||||
wine_region_node, _ = GuideElement.objects.get_or_create_wine_region_node(
|
||||
root_node.id,
|
||||
wine.wine_region.id)
|
||||
if wine_region_node:
|
||||
yard_node, _ = GuideElement.objects.get_or_create_yard_node(
|
||||
product_id=wine.id,
|
||||
wine_region_node_id=wine_region_node.id
|
||||
)
|
||||
if yard_node:
|
||||
wine_color_qs = wine.wine_colors
|
||||
if wine_color_qs.exists():
|
||||
wine_color_section, _ = GuideElement.objects.get_or_create_color_wine_section_node(
|
||||
wine_color_name=wine_color_qs.first().value,
|
||||
yard_node_id=yard_node.id
|
||||
)
|
||||
if wine_color_section:
|
||||
GuideElement.objects.get_or_create_wine_node(
|
||||
*get_additional_product_data(
|
||||
section_node=wine_color_section,
|
||||
product=wine))
|
||||
else:
|
||||
logger.error(
|
||||
f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - Wine {wine.id} has not colors.')
|
||||
else:
|
||||
logger.error(
|
||||
f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - WineRegionNode is not exists.')
|
||||
else:
|
||||
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - RootNode is not exists.')
|
||||
else:
|
||||
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - Product {wine_id} id is not exists.')
|
||||
except Exception as e:
|
||||
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||
else:
|
||||
guide.update_count_related_objects()
|
||||
# Update tree indexes
|
||||
GuideElement._tree_manager.rebuild()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ urlpatterns = [
|
|||
name='guide-list-create'),
|
||||
path('guides/<int:pk>/', views.GuideElementListView.as_view(),
|
||||
name='guide-element-list'),
|
||||
path('guides/<int:pk>/element/<int:element_pk>/advertorial/', views.AdvertorialCreateDestroyView.as_view(),
|
||||
name='guide-advertorial-create-destroy'),
|
||||
path('guides/<int:pk>/filters/', views.GuideFilterCreateView.as_view(),
|
||||
name='guide-filter-list-create'),
|
||||
] + router.urls
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ class GuideElementBaseView(generics.GenericAPIView):
|
|||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
|
||||
class AdvertorialBaseView(generics.GenericAPIView):
|
||||
"""Base view for Advertorial model."""
|
||||
pagination_class = None
|
||||
queryset = models.Advertorial.objects.all()
|
||||
serializer_class = serializers.AdvertorialBaseSerializer
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
|
||||
class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
|
|
@ -118,3 +126,9 @@ class GuideElementListView(GuideElementBaseView,
|
|||
guide = get_object_or_404(models.Guide.objects.all(), pk=self.kwargs.get('pk'))
|
||||
return models.GuideElement.objects.get_root_node(guide) \
|
||||
.get_descendants()
|
||||
|
||||
|
||||
class AdvertorialCreateDestroyView(AdvertorialBaseView,
|
||||
generics.CreateAPIView,
|
||||
generics.DestroyAPIView):
|
||||
"""View for model Advertorial for back office users."""
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class ProductQuerySet(models.QuerySet):
|
|||
return self.filter(category=self.model.ONLINE)
|
||||
|
||||
def wines(self):
|
||||
return self.filter(type__index_name__icontains=ProductType.WINE)
|
||||
return self.filter(product_type__index_name__icontains=ProductType.WINE)
|
||||
|
||||
def without_current_product(self, current_product: str):
|
||||
"""Exclude by current product."""
|
||||
|
|
@ -383,6 +383,11 @@ class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
|
|||
if self.main_image:
|
||||
return self.main_image.get_image_url(thumbnail_key='product_preview')
|
||||
|
||||
@property
|
||||
def wine_region(self):
|
||||
if self.wine_origins.exists():
|
||||
return self.wine_origins.first().wine_region
|
||||
|
||||
|
||||
class OnlineProductManager(ProductManager):
|
||||
"""Extended manger for OnlineProduct model."""
|
||||
|
|
|
|||
|
|
@ -179,3 +179,21 @@ class UnprocessableEntityError(exceptions.APIException):
|
|||
"""
|
||||
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
default_detail = _('Unprocessable entity valid.')
|
||||
|
||||
|
||||
class GuideElementError(exceptions.APIException):
|
||||
"""
|
||||
The exception should be raised when user tries send guide element that doesn't
|
||||
valid for guide.
|
||||
"""
|
||||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_detail = _('Guide element not valid for Guide.')
|
||||
|
||||
|
||||
class AdvertorialError(exceptions.APIException):
|
||||
"""
|
||||
The exception should be raised when user tries create advertorial for guide element
|
||||
that already exists.
|
||||
"""
|
||||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_detail = _('Advertorial already exists for this guide element.')
|
||||
|
|
|
|||
|
|
@ -140,4 +140,18 @@ def dictfetchall(cursor):
|
|||
return [
|
||||
dict(zip(columns, row))
|
||||
for row in cursor.fetchall()
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def slug_into_section_name(slug: str, postfix: str = 'SectionNode'):
|
||||
"""
|
||||
Transform slug into section name, i.e:
|
||||
like
|
||||
"EffervescentRoseDeSaigneeSectionNode"
|
||||
from
|
||||
"effervescent-rose-de-saignee"
|
||||
"""
|
||||
re_exp = r'[\w]+'
|
||||
result = re.findall(re_exp, slug)
|
||||
if result:
|
||||
return f"{''.join([i.capitalize() for i in result])}{postfix}"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user