see changes

This commit is contained in:
Anatoly 2019-12-26 10:24:08 +03:00
parent cbe03b2f5a
commit 1189b6ff58
8 changed files with 182 additions and 84 deletions

View File

@ -0,0 +1,49 @@
# Generated by Django 2.2.7 on 2019-12-26 06:21
from django.db import migrations, models
GUIDE_TYPE_RESTAURANT = 0
GUIDE_TYPE_WINE = 1
def transform_guide_type(apps, schema_editor):
Guide = apps.get_model('collection', 'Guide')
to_update = []
for guide in Guide.objects.all():
if guide.guide_type.name.startswith('restaurant'):
guide.guide_type_2 = GUIDE_TYPE_RESTAURANT
elif guide.guide_type.name.startswith('wine'):
guide.guide_type_2 = GUIDE_TYPE_WINE
Guide.objects.bulk_update(to_update, ['guide_type_2', ])
class Migration(migrations.Migration):
dependencies = [
('collection', '0030_guidefilter_guide_type'),
]
operations = [
migrations.RemoveField(
model_name='guidefilter',
name='guide_type',
),
migrations.AddField(
model_name='guide',
name='guide_type_2',
field=models.PositiveSmallIntegerField(choices=[(0, 'Restaurant'), (1, 'Artisan'), (2, 'Wine')], default=0, verbose_name='guide type'),
),
migrations.RunPython(transform_guide_type, migrations.RunPython.noop),
migrations.RemoveField(
model_name='guide',
name='guide_type',
),
migrations.DeleteModel(
name='GuideType',
),
migrations.RenameField(
model_name='guide',
old_name='guide_type_2',
new_name='guide_type',
),
]

View File

@ -4,20 +4,22 @@ from django.contrib.contenttypes.fields import ContentType
from django.contrib.postgres.fields import JSONField
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel
from mptt.models import MPTTModel, TreeForeignKey
from slugify import slugify
from establishment.models import Establishment
from location.models import Country, Region, WineRegion, WineSubRegion, City
from review.models import Review
from product.models import Product
from review.models import Review
from collection import tasks
from translation.models import Language
from utils.methods import slug_into_section_name
from utils.models import (
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
URLImageMixin, IntermediateGalleryModelMixin
)
from utils.methods import slug_into_section_name
from utils.querysets import RelatedObjectsCountMixin
@ -158,28 +160,6 @@ class Collection(ProjectBaseMixin, CollectionDateMixin,
super(Collection, self).save(*args, **kwargs)
class GuideTypeQuerySet(models.QuerySet):
"""QuerySet for model GuideType."""
class GuideType(ProjectBaseMixin):
"""GuideType model."""
name = models.SlugField(max_length=255, unique=True,
verbose_name=_('code'))
objects = GuideTypeQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('guide type')
verbose_name_plural = _('guide types')
def __str__(self):
"""Overridden str dunder method."""
return self.name
class GuideQuerySet(models.QuerySet):
"""QuerySet for Guide."""
@ -268,6 +248,19 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
(BUILDING, 'building'),
)
RESTAURANT = 0
ARTISAN = 1
WINE = 2
GUIDE_TYPE_CHOICES = (
(RESTAURANT, _('Restaurant')),
(ARTISAN, _('Artisan')),
(WINE, _('Wine')),
)
guide_type = models.PositiveSmallIntegerField(choices=GUIDE_TYPE_CHOICES,
default=RESTAURANT,
verbose_name=_('guide type'))
start = models.DateTimeField(null=True,
verbose_name=_('start'))
vintage = models.IntegerField(validators=[MinValueValidator(1900),
@ -276,9 +269,6 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
verbose_name=_('guide vintage year'))
slug = models.SlugField(max_length=255, unique=True, null=True,
verbose_name=_('slug'))
guide_type = models.ForeignKey('GuideType', on_delete=models.PROTECT,
null=True,
verbose_name=_('type'))
site = models.ForeignKey('main.SiteSettings', on_delete=models.SET_NULL,
null=True,
verbose_name=_('site settings'))
@ -301,6 +291,16 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
"""String method."""
return f'{self.name}'
def save(self, *args, **kwargs):
if not self.pk:
if not self.slug:
slugify_slug = slugify(
f'{self.name} {self.vintage}',
word_boundary=True
)
self.slug = slugify_slug
super(Guide, self).save(*args, **kwargs)
# todo: for test use, use annotation instead
# @property
# def restaurant_counter_prop(self):
@ -351,6 +351,43 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
self.count_related_objects = updated_count
self.save()
def generate_elements(self):
if self.guidefilter:
if self.guide_type in [self.ARTISAN, self.RESTAURANT]:
if settings.USE_CELERY:
tasks.generate_establishment_guide_elements.delay(
guide_id=self.id,
queryset_values=self.guidefilter.filtered_queryset_values,
section_node_name='RestaurantSectionNode' if self.RESTAURANT else self.ARTISAN
)
else:
tasks.generate_establishment_guide_elements(
guide_id=self.id,
queryset_values=self.guidefilter.filtered_queryset_values,
section_node_name='RestaurantSectionNode' if self.RESTAURANT else self.ARTISAN
)
elif self.guide_type == self.WINE:
if settings.USE_CELERY:
tasks.generate_product_guide_elements.delay(
guide_id=self.id,
queryset_values=self.guidefilter.filtered_queryset_values,
)
else:
tasks.generate_product_guide_elements(
guide_id=self.id,
queryset_values=self.guidefilter.filtered_queryset_values,
)
def regenerate_elements(self):
# get Root node
root_node = GuideElement.objects.get_root_node(self)
# get all descendants related to this guide and delete it
root_node.get_descendants().delete()
# re-generate elements
self.generate_elements()
# update count elements
self.update_count_related_objects()
class AdvertorialQuerySet(models.QuerySet):
"""QuerySet for model Advertorial."""
@ -384,19 +421,6 @@ 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,
@ -645,11 +669,15 @@ class GuideFilter(ProjectBaseMixin):
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
def filtered_queryset_values(self):
if self.guide.guide_type in [self.guide.RESTAURANT, self.guide.ARTISAN]:
fields = Establishment._meta.get_all_field_names()
fields.pop('tz')
return Establishment.objects.filter(**self.establishment_filter_set) \
.values_list()[0]
elif self.guide.guide_type == self.guide.WINE:
return Product.objects.filter(**self.product_filter_set) \
.values()[0]
class GuideElementType(models.Model):

View File

@ -1,14 +1,14 @@
from django.shortcuts import get_object_or_404
from rest_framework import serializers
from rest_framework_recursive.fields import RecursiveField
from collection import models
from establishment.serializers import EstablishmentGuideElementSerializer
from location import models as location_models
from main.serializers import SiteShortSerializer
from utils.serializers import TranslatedField
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
from utils.serializers import TranslatedField
class CollectionBaseSerializer(serializers.ModelSerializer):
@ -53,18 +53,6 @@ class CollectionSerializer(CollectionBaseSerializer):
]
class GuideTypeBaseSerializer(serializers.ModelSerializer):
"""GuideType serializer."""
class Meta:
"""Meta class."""
model = models.GuideType
fields = [
'id',
'name',
]
class GuideFilterBaseSerialzer(serializers.ModelSerializer):
"""Serializer for model GuideFilter."""
@ -89,8 +77,7 @@ class GuideBaseSerializer(serializers.ModelSerializer):
"""Guide serializer"""
state_display = serializers.CharField(source='get_state_display',
read_only=True)
guide_type_detail = GuideTypeBaseSerializer(read_only=True,
source='guide_type')
guide_type_display = serializers.CharField(read_only=True)
site_detail = SiteShortSerializer(read_only=True,
source='site')
guide_filters = GuideFilterBaseSerialzer(read_only=True,
@ -112,7 +99,7 @@ class GuideBaseSerializer(serializers.ModelSerializer):
'vintage',
'slug',
'guide_type',
'guide_type_detail',
'guide_type_display',
'site',
'site_detail',
'state',
@ -128,8 +115,9 @@ class GuideBaseSerializer(serializers.ModelSerializer):
'site': {'write_only': True},
'state': {'write_only': True},
'start': {'required': True},
'slug': {'required': True},
'count_objects_during_init': {'read_only': True}
'slug': {'required': False},
'count_objects_during_init': {'read_only': True},
'vintage': {'required': True},
}
@ -155,7 +143,7 @@ class GuideFilterBaseSerializer(serializers.ModelSerializer):
'guide',
]
extra_kwargs = {
'guide': {'write_only': True}
'guide': {'write_only': True, 'required': False},
}
@property
@ -168,8 +156,9 @@ class GuideFilterBaseSerializer(serializers.ModelSerializer):
guide = get_object_or_404(models.Guide.objects.all(),
pk=self.request_kwargs.get('pk'))
validated_data['guide'] = guide
return super().create(validated_data)
guide_filter = super().create(validated_data)
guide.generate_elements()
return guide_filter
class GuideElementBaseSerializer(serializers.ModelSerializer):
@ -230,7 +219,7 @@ class AdvertorialBaseSerializer(serializers.ModelSerializer):
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_qs = root_node.get_descendants().filter(pk=self.request_kwargs.get('element_pk'))
guide_element = guide_element_qs.first()
if not guide_element_qs.exists():

View File

@ -71,8 +71,6 @@ def generate_establishment_guide_elements(guide_id: int, queryset_values: dict,
f'DETAIL: Guide ID {guide_id} - {e}')
else:
guide.update_count_related_objects()
# Update tree indexes
GuideElement._tree_manager.rebuild()
@shared_task
@ -129,5 +127,3 @@ def generate_product_guide_elements(guide_id: int, queryset_values: dict):
f'DETAIL: Guide ID {guide_id} - {e}')
else:
guide.update_count_related_objects()
# Update tree indexes
GuideElement._tree_manager.rebuild()

View File

@ -14,8 +14,14 @@ 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>/regenerate/', views.GuideUpdateView.as_view(),
name='guide-regenerate'),
path('guides/<int:pk>/element/<int:element_pk>/advertorial/',
views.AdvertorialCreateView.as_view(),
name='guide-advertorial-create'),
path('guides/<int:pk>/element/<int:element_pk>/advertorial/<int:advertorial_pk>/',
views.AdvertorialDestroyView.as_view(),
name='guide-advertorial-destroy'),
path('guides/<int:pk>/filters/', views.GuideFilterCreateView.as_view(),
name='guide-filter-list-create'),
] + router.urls

View File

@ -36,7 +36,8 @@ class GuideBaseView(generics.GenericAPIView):
.annotate_restaurant_counter() \
.annotate_shop_counter() \
.annotate_wine_counter() \
.annotate_present_objects_counter()
.annotate_present_objects_counter() \
.distinct()
class GuideFilterBaseView(generics.GenericAPIView):
@ -131,7 +132,32 @@ class GuideElementListView(GuideElementBaseView,
return models.GuideElement.objects.none()
class AdvertorialCreateDestroyView(AdvertorialBaseView,
generics.CreateAPIView,
generics.DestroyAPIView):
class GuideUpdateView(GuideBaseView):
"""View for model GuideElement for back office users."""
def post(self, request, *args, **kwargs):
"""POST-method to regenerate elements of guide instance."""
guide = get_object_or_404(models.Guide.objects.all(),
pk=self.kwargs.get('pk'))
guide.regenerate_elements()
return Response(status=status.HTTP_200_OK)
class AdvertorialCreateView(AdvertorialBaseView,
generics.CreateAPIView):
"""View for model Advertorial for back office users."""
def post(self, request, *args, **kwargs):
"""Overridden post method."""
super(AdvertorialCreateView, self).create(request, *args, **kwargs)
return Response(status=status.HTTP_200_OK)
class AdvertorialDestroyView(AdvertorialBaseView,
generics.DestroyAPIView):
"""View for model Advertorial for back office users."""
lookup_url_kwarg = 'advertorial_pk'
def delete(self, request, *args, **kwargs):
"""Overridden delete method."""
return self.destroy(request, *args, **kwargs)

View File

@ -20,7 +20,6 @@ from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField
from timezone_field import TimeZoneField
from collection.models import Collection
from location.models import Address
from location.models import WineOriginAddressMixin
from main.models import Award, Currency
@ -200,6 +199,8 @@ class EstablishmentQuerySet(models.QuerySet):
If establishments in collection POP and its mark is null, then
intermediate_mark is set to 10;
"""
from collection.models import Collection
return self.annotate(intermediate_public_mark=Case(
When(
collections__collection_type=Collection.POP,

View File

@ -61,8 +61,11 @@ class GuideSerializer(TransferSerializerMixin):
return qs.first()
def get_guide_type(self, inserter_field):
guide_type, _ = models.GuideType.objects.get_or_create(name=inserter_field)
return guide_type
if inserter_field:
if inserter_field.startswith('restaurant'):
return models.Guide.RESTAURANT
elif inserter_field.startswith('wine'):
return models.Guide.WINE
class GuideFilterSerializer(TransferSerializerMixin):