added wine origins

This commit is contained in:
Anatoly 2019-12-04 20:13:09 +03:00
parent 627f7562ec
commit 29ff10fb4b
14 changed files with 294 additions and 27 deletions

View File

@ -136,3 +136,4 @@ class SocialNetworkAdmin(BaseModelAdminMixin, admin.ModelAdmin):
class CompanyAdmin(BaseModelAdminMixin, admin.ModelAdmin): class CompanyAdmin(BaseModelAdminMixin, admin.ModelAdmin):
"""Admin conf for Company model.""" """Admin conf for Company model."""
raw_id_fields = ['establishment', 'address', ] raw_id_fields = ['establishment', 'address', ]

View File

@ -0,0 +1,30 @@
from django.core.management.base import BaseCommand
from location.models import WineOriginAddress, EstablishmentWineOriginAddress
from product.models import Product
class Command(BaseCommand):
help = 'Add to establishment wine origin object.'
def handle(self, *args, **kwarg):
create_counter = 0
for product in Product.objects.exclude(establishment__isnull=True):
establishment = product.establishment
if product.wine_origins.exists():
for wine_origin in product.wine_origins.all():
wine_region = wine_origin.wine_region
wine_sub_region = wine_origin.wine_sub_region
if not EstablishmentWineOriginAddress.objects.filter(establishment=establishment,
wine_region=wine_region,
wine_sub_region=wine_sub_region) \
.exists():
EstablishmentWineOriginAddress.objects.create(
establishment=establishment,
wine_region=wine_origin.wine_region,
wine_sub_region=wine_origin.wine_sub_region,
)
create_counter += 1
self.stdout.write(self.style.WARNING(f'COUNT CREATED OBJECTS: {create_counter}'))

View File

@ -1,8 +1,8 @@
"""Establishment models.""" """Establishment models."""
from datetime import datetime from datetime import datetime
from functools import reduce from functools import reduce
from typing import List
from operator import or_ from operator import or_
from typing import List
import elasticsearch_dsl import elasticsearch_dsl
from django.conf import settings from django.conf import settings
@ -22,6 +22,7 @@ from timezone_field import TimeZoneField
from collection.models import Collection from collection.models import Collection
from location.models import Address from location.models import Address
from location.models import WineOriginAddressMixin
from main.models import Award, Currency from main.models import Award, Currency
from review.models import Review from review.models import Review
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
@ -590,6 +591,10 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
if qs.exists(): if qs.exists():
return qs.first().image return qs.first().image
@property
def wine_origins_unique(self):
return self.wine_origins.distinct('wine_region')
class EstablishmentNoteQuerySet(models.QuerySet): class EstablishmentNoteQuerySet(models.QuerySet):
"""QuerySet for model EstablishmentNote.""" """QuerySet for model EstablishmentNote."""

View File

@ -16,6 +16,8 @@ from utils import exceptions as utils_exceptions
from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer
from utils.serializers import (ProjectModelSerializer, TranslatedField, from utils.serializers import (ProjectModelSerializer, TranslatedField,
FavoritesCreateSerializer) FavoritesCreateSerializer)
from location.serializers import EstablishmentWineRegionBaseSerializer, \
EstablishmentWineOriginBaseSerializer
class ContactPhonesSerializer(serializers.ModelSerializer): class ContactPhonesSerializer(serializers.ModelSerializer):
@ -283,6 +285,8 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes')
image = serializers.URLField(source='image_url', read_only=True) image = serializers.URLField(source='image_url', read_only=True)
wine_regions = EstablishmentWineRegionBaseSerializer(many=True, source='wine_origins_unique',
read_only=True, allow_null=True)
preview_image = serializers.URLField(source='preview_image_url', preview_image = serializers.URLField(source='preview_image_url',
allow_null=True, allow_null=True,
read_only=True) read_only=True)
@ -311,6 +315,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
'image', 'image',
'preview_image', 'preview_image',
'new_image', 'new_image',
'wine_regions',
] ]
@ -365,6 +370,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
range_price_carte = RangePriceSerializer(read_only=True) range_price_carte = RangePriceSerializer(read_only=True)
vintage_year = serializers.ReadOnlyField() vintage_year = serializers.ReadOnlyField()
gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True)
wine_origins = EstablishmentWineOriginBaseSerializer(many=True, read_only=True)
class Meta(EstablishmentBaseSerializer.Meta): class Meta(EstablishmentBaseSerializer.Meta):
"""Meta class.""" """Meta class."""
@ -391,6 +397,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
'transportation', 'transportation',
'vintage_year', 'vintage_year',
'gallery', 'gallery',
'wine_origins',
] ]

View File

@ -45,3 +45,15 @@ class AddressAdmin(admin.OSMGeoAdmin):
def geo_lat(self, item): def geo_lat(self, item):
if isinstance(item.coordinates, Point): if isinstance(item.coordinates, Point):
return item.coordinates.y return item.coordinates.y
@admin.register(models.EstablishmentWineOriginAddress)
class EstablishmentWineOriginAddress(admin.ModelAdmin):
"""Admin model for EstablishmentWineOriginAddress."""
raw_id_fields = ['establishment', ]
@admin.register(models.WineOriginAddress)
class WineOriginAddress(admin.ModelAdmin):
"""Admin page for model WineOriginAddress."""
raw_id_fields = ['product', ]

View File

@ -0,0 +1,42 @@
# Generated by Django 2.2.7 on 2019-12-04 14:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('establishment', '0067_auto_20191122_1244'),
('product', '0019_auto_20191204_1420'),
('location', '0030_auto_20191120_1010'),
]
operations = [
migrations.CreateModel(
name='WineOriginAddress',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wine_origins', to='product.Product', verbose_name='product')),
('wine_region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.WineRegion', verbose_name='wine region')),
('wine_sub_region', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='location.WineSubRegion', verbose_name='wine sub region')),
],
options={
'verbose_name': 'wine origin address',
'verbose_name_plural': 'wine origin addresses',
},
),
migrations.CreateModel(
name='EstablishmentWineOriginAddress',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wine_origins', to='establishment.Establishment', verbose_name='product')),
('wine_region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='location.WineRegion', verbose_name='wine region')),
('wine_sub_region', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='location.WineSubRegion', verbose_name='wine sub region')),
],
options={
'verbose_name': 'establishment wine origin address',
'verbose_name_plural': 'establishment wine origin addresses',
},
),
]

View File

@ -192,9 +192,9 @@ class WineRegionQuerySet(models.QuerySet):
def with_sub_region_related(self): def with_sub_region_related(self):
return self.prefetch_related('wine_sub_region') return self.prefetch_related('wine_sub_region')
def having_wines(self, value = True): def having_wines(self, value=True):
"""Return qs with regions, which have any wine related to them""" """Return qs with regions, which have any wine related to them"""
return self.exclude(wines__isnull=value) return self.exclude(wineoriginaddress__product__isnull=value)
class WineRegion(TranslatedFieldsMixin, models.Model): class WineRegion(TranslatedFieldsMixin, models.Model):
@ -278,6 +278,55 @@ class WineVillage(models.Model):
return self.name return self.name
class WineOriginAddressMixin(models.Model):
"""Model for wine origin address."""
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.CASCADE,
verbose_name=_('wine region'))
wine_sub_region = models.ForeignKey('location.WineSubRegion', on_delete=models.CASCADE,
blank=True, null=True, default=None,
verbose_name=_('wine sub region'))
class Meta:
"""Meta class."""
abstract = True
class EstablishmentWineOriginAddressQuerySet(models.QuerySet):
"""QuerySet for EstablishmentWineOriginAddress model."""
class EstablishmentWineOriginAddress(WineOriginAddressMixin):
"""Establishment wine origin address model."""
establishment = models.ForeignKey('establishment.Establishment', on_delete=models.CASCADE,
related_name='wine_origins',
verbose_name=_('product'))
objects = EstablishmentWineOriginAddressQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('establishment wine origin address')
verbose_name_plural = _('establishment wine origin addresses')
class WineOriginAddressQuerySet(models.QuerySet):
"""QuerySet for WineOriginAddress model."""
class WineOriginAddress(WineOriginAddressMixin):
"""Wine origin address model."""
product = models.ForeignKey('product.Product', on_delete=models.CASCADE,
related_name='wine_origins',
verbose_name=_('product'))
objects = WineOriginAddressQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name = _('wine origin address')
verbose_name_plural = _('wine origin addresses')
# todo: Make recalculate price levels # todo: Make recalculate price levels
@receiver(post_save, sender=Country) @receiver(post_save, sender=Country)
def run_recalculate_price_levels(sender, instance, **kwargs): def run_recalculate_price_levels(sender, instance, **kwargs):

View File

@ -191,10 +191,58 @@ class WineSubRegionBaseSerializer(serializers.ModelSerializer):
] ]
class WineRegionSerializer(WineRegionBaseSerializer): class EstablishmentWineRegionBaseSerializer(serializers.ModelSerializer):
"""Wine region w/ subregion serializer""" """Establishment wine region origin serializer."""
wine_sub_region = WineSubRegionBaseSerializer(allow_null=True, many=True) id = serializers.IntegerField(source='wine_region.id')
name = serializers.CharField(source='wine_region.name')
country = CountrySerializer(source='wine_region.country')
class Meta:
"""Meta class."""
model = models.EstablishmentWineOriginAddress
fields = [
'id',
'name',
'country',
]
class EstablishmentWineOriginBaseSerializer(serializers.ModelSerializer):
"""Serializer for intermediate model EstablishmentWineOrigin."""
wine_region = WineRegionBaseSerializer()
wine_sub_region = WineSubRegionBaseSerializer(allow_null=True)
class Meta:
"""Meta class."""
model = models.EstablishmentWineOriginAddress
fields = [
'wine_region',
'wine_sub_region',
]
class WineOriginRegionBaseSerializer(EstablishmentWineRegionBaseSerializer):
"""Product wine region origin serializer."""
class Meta(EstablishmentWineRegionBaseSerializer.Meta):
"""Meta class."""
model = models.WineOriginAddress
class WineOriginBaseSerializer(EstablishmentWineOriginBaseSerializer):
"""Serializer for intermediate model ProductWineOrigin."""
class Meta(EstablishmentWineOriginBaseSerializer.Meta):
"""Meta class."""
model = models.WineOriginAddress
class WineRegionSerializer(serializers.ModelSerializer):
"""Wine region w/ sub region serializer"""
wine_sub_region = WineSubRegionBaseSerializer(source='wine_region.wine_sub_region',
allow_null=True, many=True)
class Meta(WineRegionBaseSerializer.Meta): class Meta(WineRegionBaseSerializer.Meta):
fields = WineRegionBaseSerializer.Meta.fields + [ fields = WineRegionBaseSerializer.Meta.fields + [

View File

@ -1,5 +1,6 @@
"""Product admin conf.""" """Product admin conf."""
from django.contrib import admin from django.contrib import admin
from utils.admin import BaseModelAdminMixin from utils.admin import BaseModelAdminMixin
from .models import Product, ProductType, ProductSubType, ProductGallery, Unit from .models import Product, ProductType, ProductSubType, ProductGallery, Unit

View File

@ -0,0 +1,44 @@
from django.core.management.base import BaseCommand
from tqdm import tqdm
from location.models import WineOriginAddress
from product.models import Product
from transfer.models import Products
from transfer.serializers.product import ProductSerializer
class Command(BaseCommand):
help = 'Add to product wine origin object.'
def handle(self, *args, **kwarg):
def get_product(old_id: int):
if old_id:
qs = Product.objects.filter(old_id=old_id)
if qs.exists():
return qs.first()
objects_to_create = []
products = Products.objects.exclude(wine_region_id__isnull=True) \
.values_list('id', 'wine_sub_region_id', 'wine_region_id')
for old_id, wine_sub_region_id, wine_region_id in tqdm(products):
product = get_product(old_id)
if product:
wine_sub_region = ProductSerializer.get_wine_sub_region(wine_sub_region_id)
wine_region = ProductSerializer.get_wine_region(wine_region_id)
if wine_region:
filters = {
'product': product,
'wine_region': wine_region}
wine_origin_address = WineOriginAddress(
product=product,
wine_region=wine_region)
if wine_sub_region:
filters.update({'wine_sub_region': wine_sub_region})
wine_origin_address.wine_sub_region = wine_sub_region
if not WineOriginAddress.objects.filter(**filters).exists():
objects_to_create.append(wine_origin_address)
WineOriginAddress.objects.bulk_create(objects_to_create)
self.stdout.write(self.style.WARNING(f'COUNT CREATED OBJECTS: {len(objects_to_create)}'))

View File

@ -0,0 +1,21 @@
# Generated by Django 2.2.7 on 2019-12-04 14:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('product', '0018_purchasedproduct'),
]
operations = [
migrations.RemoveField(
model_name='product',
name='wine_region',
),
migrations.RemoveField(
model_name='product',
name='wine_sub_region',
),
]

View File

@ -2,11 +2,12 @@
from django.contrib.contenttypes import fields as generic from django.contrib.contenttypes import fields as generic
from django.contrib.gis.db import models as gis_models from django.contrib.gis.db import models as gis_models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Case, When from django.db.models import Case, When
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.validators import MaxValueValidator, MinValueValidator
from location.models import WineOriginAddressMixin
from utils.models import (BaseAttributes, ProjectBaseMixin, HasTagsMixin, from utils.models import (BaseAttributes, ProjectBaseMixin, HasTagsMixin,
TranslatedFieldsMixin, TJSONField, FavoritesMixin, TranslatedFieldsMixin, TJSONField, FavoritesMixin,
GalleryModelMixin, IntermediateGalleryModelMixin) GalleryModelMixin, IntermediateGalleryModelMixin)
@ -89,8 +90,8 @@ class ProductQuerySet(models.QuerySet):
'establishment__address__city', 'establishment__address__city__country', 'establishment__address__city', 'establishment__address__city__country',
'establishment__establishment_subtypes', 'product_gallery', 'establishment__establishment_subtypes', 'product_gallery',
'gallery', 'product_type', 'subtypes', 'gallery', 'product_type', 'subtypes',
'classifications__classification_type', 'classifications__tags') \ 'classifications__classification_type', 'classifications__tags',
.select_related('wine_region', 'wine_sub_region') 'wine_origins__wine_region', 'wine_origins__wine_sub_region', )
def common(self): def common(self):
return self.filter(category=self.model.COMMON) return self.filter(category=self.model.COMMON)
@ -176,14 +177,6 @@ class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes,
verbose_name=_('establishment')) verbose_name=_('establishment'))
public_mark = models.PositiveIntegerField(blank=True, null=True, default=None, public_mark = models.PositiveIntegerField(blank=True, null=True, default=None,
verbose_name=_('public mark'), ) verbose_name=_('public mark'), )
wine_region = models.ForeignKey('location.WineRegion', on_delete=models.PROTECT,
related_name='wines',
blank=True, null=True, default=None,
verbose_name=_('wine region'))
wine_sub_region = models.ForeignKey('location.WineSubRegion', on_delete=models.PROTECT,
related_name='wines',
blank=True, null=True, default=None,
verbose_name=_('wine sub region'))
classifications = models.ManyToManyField('ProductClassification', classifications = models.ManyToManyField('ProductClassification',
blank=True, blank=True,
verbose_name=_('classifications')) verbose_name=_('classifications'))

View File

@ -11,7 +11,7 @@ from review.serializers import ReviewShortSerializer
from utils import exceptions as utils_exceptions from utils import exceptions as utils_exceptions
from utils.serializers import TranslatedField, FavoritesCreateSerializer, ImageBaseSerializer from utils.serializers import TranslatedField, FavoritesCreateSerializer, ImageBaseSerializer
from main.serializers import AwardSerializer from main.serializers import AwardSerializer
from location.serializers import WineRegionBaseSerializer, WineSubRegionBaseSerializer from location.serializers import WineOriginRegionBaseSerializer, WineOriginBaseSerializer
from tag.serializers import TagBaseSerializer, TagCategoryProductSerializer from tag.serializers import TagBaseSerializer, TagCategoryProductSerializer
@ -90,7 +90,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
subtypes = ProductSubTypeBaseSerializer(many=True, read_only=True) subtypes = ProductSubTypeBaseSerializer(many=True, read_only=True)
establishment_detail = EstablishmentProductShortSerializer(source='establishment', read_only=True) establishment_detail = EstablishmentProductShortSerializer(source='establishment', read_only=True)
tags = ProductTagSerializer(source='related_tags', many=True, read_only=True) tags = ProductTagSerializer(source='related_tags', many=True, read_only=True)
wine_region = WineRegionBaseSerializer(read_only=True) wine_regions = WineOriginRegionBaseSerializer(many=True, source='wine_origins', read_only=True)
wine_colors = TagBaseSerializer(many=True, read_only=True) wine_colors = TagBaseSerializer(many=True, read_only=True)
preview_image_url = serializers.URLField(allow_null=True, preview_image_url = serializers.URLField(allow_null=True,
read_only=True) read_only=True)
@ -110,7 +110,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
'vintage', 'vintage',
'tags', 'tags',
'preview_image_url', 'preview_image_url',
'wine_region', 'wine_regions',
'wine_colors', 'wine_colors',
'in_favorites', 'in_favorites',
] ]
@ -124,7 +124,7 @@ class ProductDetailSerializer(ProductBaseSerializer):
awards = AwardSerializer(many=True, read_only=True) awards = AwardSerializer(many=True, read_only=True)
classifications = ProductClassificationBaseSerializer(many=True, read_only=True) classifications = ProductClassificationBaseSerializer(many=True, read_only=True)
standards = ProductStandardBaseSerializer(many=True, read_only=True) standards = ProductStandardBaseSerializer(many=True, read_only=True)
wine_sub_region = WineSubRegionBaseSerializer(read_only=True) wine_origins = WineOriginBaseSerializer(many=True, read_only=True)
bottles_produced = TagBaseSerializer(many=True, read_only=True) bottles_produced = TagBaseSerializer(many=True, read_only=True)
sugar_contents = TagBaseSerializer(many=True, read_only=True) sugar_contents = TagBaseSerializer(many=True, read_only=True)
grape_variety = TagBaseSerializer(many=True, read_only=True) grape_variety = TagBaseSerializer(many=True, read_only=True)
@ -141,7 +141,7 @@ class ProductDetailSerializer(ProductBaseSerializer):
'awards', 'awards',
'classifications', 'classifications',
'standards', 'standards',
'wine_sub_region', 'wine_origins',
'bottles_produced', 'bottles_produced',
'sugar_contents', 'sugar_contents',
'image_url', 'image_url',

View File

@ -340,6 +340,9 @@ class ProductSerializer(TransferSerializerMixin):
def create(self, validated_data): def create(self, validated_data):
qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id')) qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id'))
wine_region = validated_data.pop('wine_region')
wine_sub_region = validated_data.pop('wine_sub_region')
# classifications # classifications
classifications = [validated_data.pop('wine_classification', None)] classifications = [validated_data.pop('wine_classification', None)]
# standards # standards
@ -361,6 +364,11 @@ class ProductSerializer(TransferSerializerMixin):
obj.standards.add(*[i for i in standards if i and i not in obj.standards.all()]) obj.standards.add(*[i for i in standards if i and i not in obj.standards.all()])
# adding tags # adding tags
obj.tags.add(*[i for i in tags if i and i not in obj.tags.all()]) obj.tags.add(*[i for i in tags if i and i not in obj.tags.all()])
# checking wine origin address
wine_origin_address, _ = location_models.WineOriginAddress.objects.get_or_create(
product=obj,
wine_region=wine_region,
wine_sub_region=wine_sub_region)
return obj return obj
def get_name(self, name, brand): def get_name(self, name, brand):
@ -390,17 +398,23 @@ class ProductSerializer(TransferSerializerMixin):
if classification_qs.exists(): if classification_qs.exists():
return classification_qs.first() return classification_qs.first()
def get_wine_region(self, wine_region): @staticmethod
def get_wine_region(wine_region):
if wine_region: if wine_region:
old_id = wine_region if not isinstance(wine_region, transfer_models.WineLocations) \
else wine_region.id
wine_region_qs = location_models.WineRegion.objects.filter( wine_region_qs = location_models.WineRegion.objects.filter(
old_id=wine_region.id) old_id=old_id)
if wine_region_qs.exists(): if wine_region_qs.exists():
return wine_region_qs.first() return wine_region_qs.first()
def get_wine_sub_region(self, wine_sub_region_id): @staticmethod
if wine_sub_region_id: def get_wine_sub_region(wine_sub_region):
if wine_sub_region:
old_id = wine_sub_region if not isinstance(wine_sub_region, transfer_models.WineLocations) \
else wine_sub_region.id
sub_region_qs = location_models.WineSubRegion.objects.filter( sub_region_qs = location_models.WineSubRegion.objects.filter(
old_id=wine_sub_region_id) old_id=old_id)
if sub_region_qs.exists(): if sub_region_qs.exists():
return sub_region_qs.first() return sub_region_qs.first()